Virtueller Zuweisungsoperator



  • fr33g schrieb:

    Also, in meinem Buch steht, der Zuweisungsoperator wird nicht vererbt.
    Wenn ich probiere durch nen Zeiger auf die abgeleitete Klasse, ein Basisobjekt zuzuweisen, dann kommt ein Fehler, die Klasse hätte keinen Zuweisungsoperator mit Referenz auf Basis als Parameter....

    Kauf Dir ein neues Buch:

    #include <stdio.h>
    
    class base
    {
        public:
        int baseMem;
    
        base &operator = ( const base &src );
    };
    
    class derived : public base
    {
        public:
        int derivedMem;
    };
    
    base &base::operator = ( const base &src )
    {
        puts( "base &base::operator = ( const base &src )\n" );
        this->baseMem = src.baseMem;
    
        return *this;
    }
    int main( void )
    {
        derived *ptr4 = new derived;
        derived *ptr5 = new derived;
    
        *ptr5 = *ptr4;
    }
    

    Es ist völlig gleichgültig ob der Zuweisungsoperator nun virtuell ist oder nicht.

    fr33g schrieb:

    Dann würde dass ja auch nicht stimmen, denn der Zuweisungsoperator wäre ja vererbt wie du sagst?

    Danke schonmal

    Gruß freeG

    Wo siehst Du dann den Widerspruch? Wenn in der abgeleiteten Klasse eine Funktion nicht überladen wird, so wird die Basisklassenversion benutzt. Unabhängig von virtuell oder nicht virtuell. Das ist doch der Sinn der Vererbung.



  • base &derived::operator = ( const base &src )
    {
        puts( "base &derived::operator = ( const base &src )" );
        this->baseMem = src.baseMem;
        const derived *dSrc = dynamic_cast<const derived *>(&src);
        if( dSrc )
        {
            this->derivedMem = dSrc->derivedMem;
            puts( "derived found" );
        }
        return *this;
    }
    

    So könnte z.B. ein überladener virtueller Zuweisungsoperator implementiert sein.

    Wer's braucht.

    mfg Martin



  • Also der C++ Primer ist doch eigentlich ein gutes Buch oder nicht?
    Ok das eine abgeleitete Klasse die Funktion der Basis benutzt wenn sie sie nicht selbst überlädt ist ja klar, egal ob virtual oder nicht.

    Aber bei deinem Beispiel, wird nur der Base-Zuweisungsoperator aufgerufen, weil der compilergenerierte Zuweisungsoperator, den Zuweisungsoperator der Basis aufruft.
    Mach das ganze mal so, dass du als rechten Operand die Basis nutzt und nicht die abgeleitete Klasse, schon erhält man eienen Compilerfehler, dass dieser Operator nicht vorhanden ist.
    Also liege ich doch richtig, dass der Zuweisungsoperator nicht vererbt wird?

    Danke schonmal

    Gruß freeG

    EDIT:
    http://www.willemer.de/informatik/cpp/cpperben.htm
    http://www.highscore.de/cpp/aufbau/vererbung.html
    http://www.mi.fh-wiesbaden.de/~barth/cpp/vorl/CPPPB9.pdf

    Dort steht überall der Zuweisungsoperator wird wie Konstruktoren und Destruktoren nie vererbt?



  • Das Thema hatten wir vor kurzem:

    Nexus schrieb:

    Nebenbei: Ein virtueller Zuweisungsoperator ist ein wenig fragwürdig. Und zwar einfach, weil C++ kein Double-Dispatching kann und der rechte Operand somit als statischer Typ interpretiert wird, was bei polymorphem Code wie

    Base* a = ...;
    Base* b = ...;
    
    *a = *b;
    

    nur die Basisklassenversion Base::operator= in Betracht zieht.

    Alternativen sind oft auch semantisch sinnvoller: Entweder, man verbietet Wertsemantik komplett (durch privaten Kopierkonstruktor und Zuweisungsoperator), oder man lässt sie auf Objekten gleichen Typs (in der gleichen Hierarchiestufe) zu, wodurch man wiederum kein virtual benötigt.



  • Ok vielen Dank, ich verstehe es jetzt einigermaßen, vll kann mich grad kurz jemand bestätigne oder verbessern, damit ichs entgültig raff:D

    Also:

    Zuweisungsoperatoren werden nicht vererbt.

    Man sollte am besten bei Zuweisungen auf Polymorphie verzichten, da der rechte Operand immer als statisch angesehen wird, daher bringt eine virtual Deklarierung der Zuweisungsoperatoren nichts, da immer nur der in Betracht kommt, wo der rechte Operand die Basis ist und das ist ja im Normalfall nur in der Basisklasse der Fall, wo anderster wäre es ja eher unlogisch.

    Wenn ich als linken Operand nen Zeiger auf die abgeleiete Klasse habe und als rechten einen auf die Basis, dann funktioniert dass nicht, da in der abgeleieten Klasse nur der Operator gefunden wird, welcher als Referenz die abgeleiete Klasse braucht, daher wird nicht mehr in der Basis gesucht sondern e s kommt ein Fehler.

    Ist der linke Operand ein Zeiger auf die Basis, zeigt aber auf eine abgeleiete Klasse und der rechte einer auf die abgeleietete Klasse, welche Version wir dann aufgerufen? Denn dann ist der statische Typ ja die abgeleite Klasse. Wird dann die version der basis aufgerufen und der rechte Operand wird konvertiert, oder die version der abgeleieten Klasse?

    Danke gruß freeG

    Hoff dann versteh ich es:D



  • fr33g schrieb:

    Mach das ganze mal so, dass du als rechten Operand die Basis nutzt und nicht die abgeleitete Klasse, schon erhält man eienen Compilerfehler, dass dieser Operator nicht vorhanden ist.
    Also liege ich doch richtig, dass der Zuweisungsoperator nicht vererbt wird?

    OK, ich sehe jetzt, wie Du's meinst. Hier verhält sich tatsächlich der Zuweisungsoperator ähnlich wie Konstruktoren und anders wie normale Funktionen. Das hat aber auch einen einfachen Grund:

    Der C++-Standard, will, daß der Quelltext gut lesbar ist. Wenn ein Programmierer folgende Zeile sieht:

    x=y;

    Darf er erwarten, daß x danach identisch mit y ist. Wenn y aber vom Typ der Basisklasse von x ist, kann der Compiler das nicht automatisch konvertieren. Er könnte zwar die Zuweisung der Basisklasse ausführen, der abgeleitete Teil ist dann aber undefiniert. Der Designer des Sprachstandards will aber nicht dafür verantwortlich sein, daß so ein Code automatisch generiert wird. Wenn Du aber selber so einen operator definierst, dann klappt es. Bist aber auch selber schuld, wenn das kein Mensch versteht.

    #include <stdio.h> 
    
    class base 
    { 
        public: 
        int baseMem; 
    
        base &operator = ( const base &src ); 
    }; 
    
    class derived : public base 
    { 
        public: 
        int derivedMem; 
        derived &operator = ( const base &src );
    }; 
    
    base &base::operator = ( const base &src ) 
    { 
        puts( "base &base::operator = ( const base &src )\n" ); 
        this->baseMem = src.baseMem; 
    
        return *this; 
    } 
    derived &derived::operator = ( const base &src ) 
    { 
        puts( "derived &derived::operator = ( const base &src )\n" ); 
        this->baseMem = src.baseMem; 
        this->derivedMem = 0; 
    
        return *this; 
    } 
    int main( void ) 
    {
        base    *ptr2 = new base;
        derived *ptr4 = new derived; 
        derived *ptr5 = new derived; 
    
        ptr5->derivedMem = 3;
        ptr4->derivedMem = 2;
        printf( "%d\n", ptr5->derivedMem );
        *ptr5 = *ptr4; 
        printf( "%d\n", ptr5->derivedMem );
        *ptr5 = *ptr2;
        printf( "%d\n", ptr5->derivedMem );
    }
    

    Jetzt das ganze mit virtuellem Zuweisungsoperator:

    #include <stdio.h> 
    
    class base 
    { 
        public: 
        int baseMem; 
    
        virtual base &operator = ( const base &src ); 
    }; 
    
    class derived : public base 
    { 
        public: 
        int derivedMem; 
        base &operator = ( const base &src );
    }; 
    
    base &base::operator = ( const base &src ) 
    { 
        puts( "base &base::operator = ( const base &src )" ); 
        this->baseMem = src.baseMem; 
    
        return *this; 
    } 
    base &derived::operator = ( const base &src ) 
    { 
        puts( "base &derived::operator = ( const base &src )" ); 
        this->baseMem = src.baseMem; 
        const derived *dSrc = dynamic_cast<const derived *>(&src);
        if( dSrc )
        {
            this->derivedMem = dSrc->derivedMem;
            puts( "derived found" );
        }
    	else
            this->derivedMem = 0;
        return *this;
    }
    
    int main( void ) 
    {
    	base	*ptr1 = new base;
        derived *ptr2 = new derived; 
        derived *ptr3 = new derived; 
    
    	base	*ptr4 = ptr3;
    
    	ptr2->derivedMem = 3;
    	ptr3->derivedMem = 2;
    	printf( "%d\n", ptr3->derivedMem );
    
    	*ptr3 = *ptr2; 
    	printf( "*ptr3 = *ptr2; %d\n", ptr3->derivedMem );
    
    	*ptr3 = *ptr1;
    	printf( "*ptr3 = *ptr1; %d\n", ptr3->derivedMem );
    
    	*ptr4 = *ptr2; 
    	printf( "*ptr4 = *ptr2; %d\n", ptr3->derivedMem );
    
    	*ptr4 = *ptr1;
    	printf( "*ptr4 = *ptr1; %d\n", ptr3->derivedMem );
    }
    

    mfg Martin



  • fr33g schrieb:

    Ok vielen Dank, ich verstehe es jetzt einigermaßen, aber kannst du mir nur kurz sagen ob jetzt der Zuweisungsoperator vererbt wird oder nicht?

    Danke gruß freeG

    Einigen wir uns einfach auf JEIN. 😃

    mfg Martin



  • Der Zuweisungsoperator wird nicht vererbt, allerdings bei fehlender Deklaration vom Compiler generiert, sofern möglich.



  • Muss jetzt weg, ich les mir das morgen früh nochmal durch was du jetzt grad geschreiben hast währen ich editiert habe:D und melde mich dann nochmal ob ichs verstanden hab;-)

    Schonmal vielen Dank

    gruß freeG



  • So hab mir das durchgelesen und kompiliert, aber irgendwie schocken mich die Ergebnise grad:D, kannst du vll kurz mal dazu ne kleine Beschreibung einfügen, wann was warum ausgeführt wird.
    Wär echt lieb.

    Danke schonmal

    Gruß freeG



  • fr33g schrieb:

    So hab mir das durchgelesen und kompiliert, aber irgendwie schocken mich die Ergebnise grad:D, kannst du vll kurz mal dazu ne kleine Beschreibung einfügen, wann was warum ausgeführt wird.

    /*
        Beispiel 1 von "mgaeckler"
    */
    
    #include <iostream>
    using namespace std;
    
    class base
    {
        public:
            base &operator = ( const base &src );
    };
    
    class derived : public base
    {
        public:
            int derivedMem;
            derived &operator = ( const base &src );
    };
    
    base &base::operator = ( const base &src )
    {
        cout << "base" << endl;
        return *this;
    }
    
    derived &derived::operator = ( const base &src )
    {
        cout << "sub" << endl;
        this->derivedMem = 0;
    
        return *this;
    }
    
    int main()
    {
        base    *ptr2 = new base;       // ptr2 : base-Zeiger auf base-Objekt
        derived *ptr4 = new derived;    // ptr4 : sub-Zeiger auf sub-Objekt
        derived *ptr5 = new derived;    // ptr5 : sub-Zeiger auf sub-Objekt
    
        ptr5->derivedMem = 3;
        ptr4->derivedMem = 2;
    
        cout << ptr5->derivedMem << endl << endl; // Ausgabe 3
    
        /*
            derived::operator=(const derived&) wird aufgerufen [ ptr5->derivedMem ist nun 2 ],
            die dann base::operator=(const base&) auruft.
        */  
        *ptr5 = *ptr4;
        cout << ptr5->derivedMem << endl << endl; // Ausgabe 2
    
        /*
            derived::operator=(const base&) wird aufgerufen [ ptr5->derivedMem ist nun 0 ].
        */  
        *ptr5 = static_cast<base>(*ptr4);
        cout << ptr5->derivedMem << endl << endl; // Ausgabe 0
    
        /*
            derived::operator=(const base&) wird aufgerufen [ ptr5->derivedMem ist nun 0 ].
        */
        *ptr5 = *ptr2;
        cout << ptr5->derivedMem << endl; // Ausgabe 0
    
        system("pause>nul");
        return 0;
    }
    


  • /*
        Beispiel 2 von "mgaeckler"
    */
    
    #include <iostream>
    using namespace std;
    
    class base
    {
        public:
            int baseMem;
            virtual base &operator = ( const base &src );
    };
    
    class derived : public base
    {
        public:
            int derivedMem;
            base &operator = ( const base &src );
    };
    
    base &base::operator = ( const base &src )
    {
        cout << "base" << endl;
        this->baseMem = src.baseMem;
    
        return *this;
    }
    
    base &derived::operator = ( const base &src )
    {
        cout << "sub" << endl;
        this->baseMem = src.baseMem;
        const derived *dSrc = dynamic_cast<const derived *>(&src); // dynamic cast möglich, da die Klasse polymorph ist.
        if( dSrc )
        {
            cout << "ok" << endl;
            this->derivedMem = dSrc->derivedMem;
        }
        else
        {
            cout << "failed" << endl;
            this->derivedMem = 0;
        }
    
        return *this;
    }
    
    int main()
    {
        base    *ptr1 = new base;       // ptr1 : base-Zeiger auf base-Objekt 
        derived *ptr2 = new derived;    // ptr2 : sub-Zeiger auf sub-Objekt 
        derived *ptr3 = new derived;    // ptr3 : sub-Zeiger auf sub-Objekt
        base    *ptr4 = ptr3;           // ptr4 : base-Zeiger auf sub-Objekt
    
        ptr2->derivedMem = 3;
        ptr3->derivedMem = 2;
        cout << ptr3->derivedMem << endl << endl; // Ausgabe 2
    
        /*
            derived::operator=(const derived&) wird aufgerufen [ ptr3->derivedMem ist nun 3],
            die dann base::operator=(const base&) auruft. 
        */
        *ptr3 = *ptr2;
        cout << ptr3->derivedMem << endl << endl; // Ausgabe base + 3
    
        /*
            derived::operator=(const base&) wird aufgerufen.
            [ 
                ptr3->derivedMem ist nun 0, da dynamic cast fehlschlägt, da "src" vom Typ "base" ist.
                    --> Zeiger können in der Hirarchie nur nach unten zeigen, und nicht nach oben.
            ]
        */
        *ptr3 = *ptr1;
        cout << ptr3->derivedMem << endl << endl; // Ausgabe sub + failed + 0
    
        /*
            Hier kommt nun "virtual" zum Tragen:
                    Der Typ des Objekts, auf das ptr4 zeigt, entscheidet. --> derived
    
            Wieso aber derived::operator=(const base&) aufgerufen wird,
            und nicht derived::operator=(const derived&), da *ptr2 ja vom Typ "derived" ist,
            kann ich nicht nachvollziehen. ?????
        */
        *ptr4 = *ptr2;
        cout << ((derived*)ptr4)->derivedMem << endl << endl; // ?????
    
        /*
            Hier kommt nun "virtual" zum Tragen:
                    Der Typ des Objekts, auf das ptr4 zeigt, entscheidet. --> derived
    
            derived::operator=(const base&) wird aufgerufen.
            [ 
                ptr4->derivedMem ist nun 0, da dynamic cast fehlschlägt, da "src" vom Typ "base" ist.
                    --> Zeiger können in der Hirarchie nur nach unten zeigen, und nicht nach oben.
            ]
        */
        *ptr4 = *ptr1;
        cout << ((derived*)ptr4)->derivedMem << endl; // Ausgabe sub + failed + 0
    
        system("pause>nul");
        return 0; 
    }
    

    Vielleicht kann einer hierzu

    /*
            Hier kommt nun "virtual" zum Tragen:
                    Der Typ des Objekts, auf das ptr4 zeigt, entscheidet. --> derived
    
            Wieso aber derived::operator=(const base&) aufgerufen wird,
            und nicht derived::operator=(const derived&), da *ptr2 ja vom Typ "derived" ist,
            kann ich nicht nachvollziehen. ?????
        */
        *ptr4 = *ptr2;
        cout << ((derived*)ptr4)->derivedMem << endl << endl; // ?????
    

    Stellung nehmen 😉



  • Hey erst mal vielen Dank, jetzt versteh ich das ganze langsam endlich:D 🙂
    Aber die eine Ausgabe verwundert mich auch, wieso dort nicht der compilergenerierte Zuweisungsoperator aufgerufen wird, sondern der mit base als parameter.

    Wär cool wenn da vielleicht jemand was zu sagen könnte.

    Auf jeden Fall schonmal Vielen Dank

    Gruß freeG



  • Jetzt wird's strange 😮

    #include <iostream>
    using namespace std;
    
    class base
    {
        public:
            virtual base &operator = ( const base &src ) {};
    };
    
    class derived : public base
    {
        public:
            virtual derived &operator = ( const base &src );
            virtual derived &operator = ( const derived &src );
    };
    
    derived &derived::operator = ( const base &src )
    {
        cout << "sub" << endl;
    }
    
    derived &derived::operator = ( const derived &src )
    {
        cout << "ddddddddddddddddddddddddddd" << endl;
    }
    
    int main()
    {
        base *ptr2 = new derived;
        base *ptr4 = new derived;
    
        /*
            error:
                    no matching function for call to derived::derived(base&)
                    candidates are:
                        derived::derived()
                        derived::derived(const derived&) 
        */
        (*ptr4).operator=( (derived)(*ptr2) ); //*ptr4 = (derived)(*ptr2);
    
        /*
            Geht, obwohl es doch dasselbe ist wie oben oO.
        */
       (*ptr4).operator=(*ptr2); 
    
        system("pause>nul");
        return 0;
    }
    


  • Es wurde in diesem und im von mir verlinkten Thread schon genügend oft gesagt, dass ein virtueller Zuweisungsoperator nicht sehr sinnvoll ist. Ihn mittels dynamic_cast zu implementieren, um halbwegs Double-Dispatch möglich zu machen, finde ich ziemlich gehackt. Da gibt es weitaus bessere Ansätze, z.B. in Alexandrescus "Modern C++ Design".

    Das Problem ist, dass Wertsemantik (und damit Dinge wie Kopierkonstruktor oder Zuweisungsoperator) nicht direkt polymorph implementiert werden kann, weil die Objekte oft inkompatibel sind und man Slicing oder sonstiges undefiniertes Verhalten leichtfertig in Kauf nimmt, wenn man versucht, gleich wie bei statischen Typen vorzugehen.

    Das heisst nicht, dass polymorphe Klassen generell keine Kopierkonstruktoren und Zuweisungsoperatoren haben dürfen, jedoch sollten diese nicht virtuell sein. Beim Konstruktor ist bereits durch die Sprache gegeben, dass der dynamische Typ des neuen Objekts bekannt sein muss, beim Zuweisungsoperator jedoch nicht, was zu solchen Experimenten verleitet. Wertsemantik ist sehr wohl möglich, aber nur auf gleicher Ebene und mit statischen Typen. Für "polymorphe Wertsemantik" stellen immer noch Smart-Pointer eine Möglichkeit dar, welche z.B. eine virtuelle Clone() -Funktion aufrufen und sicherstellen, dass kein Slicing zum Tragen kommt.



  • Dweb schrieb:

    Vielleicht kann einer hierzu

    /*
            Hier kommt nun "virtual" zum Tragen:
                    Der Typ des Objekts, auf das ptr4 zeigt, entscheidet. --> derived
                    
            Wieso aber derived::operator=(const base&) aufgerufen wird,
            und nicht derived::operator=(const derived&), da *ptr2 ja vom Typ "derived" ist,
            kann ich nicht nachvollziehen. ?????
        */
        *ptr4 = *ptr2;
        cout << ((derived*)ptr4)->derivedMem << endl << endl; // ?????
    

    Stellung nehmen 😉

    Du erwartest von virtuellen Funktionen Dinge, die sie schlichtweg nicht können.

    Polymorphie ist nur möglich, wenn die Signatur der Funktion in abgeleiteten und Basisklassen gleich ist. Die Parametertypen dürfen nicht kovariant sein. Wenn somit eine virtuelle Funktion

    virtual base& base::operator= (const base&);
    

    aufgerufen wird, kommt für linke Operanden vom dynamischen Typ derived nur der Zuweisungsoperator

    virtual derived& derived::operator= (const base&);
    

    in Frage.

    Das habe ich übrigens in meinem Post erklärt. Schade, dass er nicht gelesen wurde.

    Nexus schrieb:

    Nebenbei: Ein virtueller Zuweisungsoperator ist ein wenig fragwürdig. Und zwar einfach, weil C++ kein Double-Dispatching kann und der rechte Operand somit als statischer Typ interpretiert wird [...]

    Dweb schrieb:

    Jetzt wird's strange 😮

    [...]
    
    derived &derived::operator = ( const base &src )
    {
        cout << "sub" << endl;
    }
    
    derived &derived::operator = ( const derived &src )
    {
        cout << "ddddddddddddddddddddddddddd" << endl;
    }
    

    Was hast du für einen Compiler, der das Fehlen des Rückgabewertes toleriert? Wenn nicht mal richtig kompiliert wird, wäre ich erst recht vorsichtig mit Interpretationen des Laufzeitverhaltens.



  • Nexus schrieb:

    Das habe ich übrigens in meinem Post erklärt. Schade, dass er nicht gelesen wurde.

    Nexus schrieb:

    Nebenbei: Ein virtueller Zuweisungsoperator ist ein wenig fragwürdig. Und zwar einfach, weil C++ kein Double-Dispatching kann und der rechte Operand somit als statischer Typ interpretiert wird [...]

    Ich habe das gelesen Nexus, aber in dem Beispiel ist der rechte Operand als statischer doch vom Typ der abgeleiteten Klasse, oder überseh ich da was?

    Gruß freeG



  • fr33g schrieb:

    Ich habe das gelesen Nexus, aber in dem Beispiel ist der rechte Operand als statischer doch vom Typ der abgeleiteten Klasse, oder überseh ich da was?

    Nein, der statische Typ ist eben die Basisklasse.

    base* dst = new derived;
    derived* src = new derived;
    
    *dst = *src;
    

    Ohne virtual würde folgende Funktion aufgerufen werden.

    base& base::operator= (const base&);
    

    Da nun aber virtuelle Funktionen im Spiel sind, wird der linke Operand (das Objekt *this ) dynamisch dispatcht. Aufgerufen wird also die überschriebene Methode

    virtual derived& derived::operator= (const base&);
    

    wie im letzten Post erklärt. Es gibt keinen Grund, wieso

    virtual derived& derived::operator= (const derived&);
    

    aufgerufen werden sollte, da diese Funktion erst in der abgeleiteten Klasse deklariert ist. Bei der Zuweisung wird jedoch aufgrund des statischen Typen base in der Basisklasse nach Überladungen gesucht.

    Aber wie gesagt: Meide virtuelle Zuweisungsoperatoren, denn sie sind ein fehleranfälliger Workaround, um nicht vorhandene Wertsemantik und Double-Dispatching im polymorphen Kontext zu lösen.



  • Dweb schrieb:

    Vielleicht kann einer hierzu

    /*
            Hier kommt nun "virtual" zum Tragen:
                    Der Typ des Objekts, auf das ptr4 zeigt, entscheidet. --> derived
                    
            Wieso aber derived::operator=(const base&) aufgerufen wird,
            und nicht derived::operator=(const derived&), da *ptr2 ja vom Typ "derived" ist,
            kann ich nicht nachvollziehen. ?????
        */
        *ptr4 = *ptr2;
        cout << ((derived*)ptr4)->derivedMem << endl << endl; // ?????
    

    Stellung nehmen 😉

    Ist doch einfach. ptr4 ist vom Typ base* also kann er nur Funktionen, die in base deklariert sind, aufrufen. Wenn sie virtuell sind, dürfen sie zwar auch in abgeleiteten Klassen definiert sein, sie müssen aber in jedem Fall in der Basisklasse deklariert sein.

    derived::operator=(const derived&) ist in der Basisklasse weder definiert noch deklariert.

    mfg Martin



  • mgaeckler schrieb:

    Ist doch einfach. ptr4 ist vom Typ base* also kann er nur Funktionen, die in base deklariert sind, aufrufen. Wenn sie virtuell sind, dürfen sie zwar auch in abgeleiteten Klassen definiert sein, sie müssen aber in jedem Fall in der Basisklasse deklariert sein.

    derived::operator=(const derived&) ist in der Basisklasse weder definiert noch deklariert.

    mfg Martin

    Nexus hat's auch schon geschrieben. Aber ich gebe mal ein weiteren Hinweis:

    überlegt euch mal:
    Der Compiler sieht:

    *ptr4 = *ptr5;
    

    Der Compiler kann nun nur an Hand des Objektes auf das ptr4 zeigt entscheiden, welche Funktion aufgerufen wird. Hierzu benutzt er eine Funktionstabelle, deren Aufbau ausschließlich von der Klasse base abhängt. Das muß deshalb so sein, weil base in jedem Kontext immer identisch sein muß. Deshalb kann base ja auch Funktionen aufrufen, von denen es nur die Deklaration aber nicht die Definition kennt. Ja sogar von abgeleiteten Klassen, die es gar nicht kennt. Das geht deshalb, weil die abgeleiteten Klassen die Struktur von base kennen und in deren Konstruktoren (oder wie auch immer der Compiler das realisiert) wird dann die Funktionstabelle entsprechend angepasset.

    Für jede Klasse gibt es eine eigene Funktionstabelle und jedes Objekt enthält einen Zeiger auf diese Funktionstabelle. (Oder wie auch immer der Compiler das realisiert)

    Die Funktionstabelle von base enthält nun definitiv keinen Eintrag für einen Zeiger auf operator = ( derived & ) , weil die Deklaration der Klasse base gar keine solche Funktion kennt sonder nur operator = ( base & ) . Wenn der Compiler die Struktur von base ermittelt betrachtet er nicht im geringsten irgendwelche abgeleiteten Klassen. Wäre auch Unfug, denn was soll er machen, wenn er die noch gar nicht kennt, weil sie in einer anderen Quelltext datei ist, oder weil sie noch gar nicht existiert?

    mfg Martin


Anmelden zum Antworten