Virtueller Zuweisungsoperator



  • fr33g schrieb:

    Ok, ja gut die beiden letzten Zuweisungen sind ja nicht wirklich sinnvoll, da doch jetzt nur der Base-Teil zugewiesen wird und der Derived-Teil der alte bleibt oder nicht?

    Korrekt.

    fr33g schrieb:

    Also wenn ich das jetzt richtig verstanden habe, dann ist Polymorphie bei Zuweisungen sehr unlogisch, da ja der Zuweisungsoperator der abgeleiten Klasse, eigentlich ne Referenz auf seine Klasse braucht und nicht auf die der Basis.

    Also einfach Zuweisungsoperatoren nicht virtuell machen und wenn man ne Zuweisung machen muss, dann Pointer auf die Klasse selbst, nicht auf die Basis!?

    Korrekt.

    fr33g schrieb:

    Aber ich bin doch der Annahme richtig, dass der Zuweisungsoperator nicht vererbt wird oder?

    Das ist falsch. Der wird genauso vererbt, wie alle anderen Memberfunktionen.

    fr33g schrieb:

    Heißt wenn ich jetzt den Zuweisungsoperator der Basis als virtual deklariere und mit einem Zeiger auf die Basis auf ein abgeleitetes Objekt zeige, dann ruft er die Basiszuweisung auf, da in der abgeleiteten Klasse kein virtual Zuweisungsoperator ist oder?

    Korrekt.

    fr33g schrieb:

    Und wenn ich das gleiche mit nem Zeiger auf die abgeleitete Klasse macht, kommt ein Fehler, da diese Klasse keinen Zuweisungsoperator besitzt, welcher als Argument ne Basisreferenz erhält, da dieser Operator ja nicht verert wurde.

    Stimmt das so?

    Das ist falsch. Er wird ja vererbt.

    mfg Martin



  • mgaeckler schrieb:

    fr33g schrieb:

    Aber ich bin doch der Annahme richtig, dass der Zuweisungsoperator nicht vererbt wird oder?

    Das ist falsch. Der wird genauso vererbt, wie alle anderen Memberfunktionen.

    fr33g schrieb:

    Heißt wenn ich jetzt den Zuweisungsoperator der Basis als virtual deklariere und mit einem Zeiger auf die Basis auf ein abgeleitetes Objekt zeige, dann ruft er die Basiszuweisung auf, da in der abgeleiteten Klasse kein virtual Zuweisungsoperator ist oder?

    Korrekt.

    fr33g schrieb:

    Und wenn ich das gleiche mit nem Zeiger auf die abgeleitete Klasse macht, kommt ein Fehler, da diese Klasse keinen Zuweisungsoperator besitzt, welcher als Argument ne Basisreferenz erhält, da dieser Operator ja nicht verert wurde.

    Stimmt das so?

    Das ist falsch. Er wird ja vererbt.

    mfg Martin

    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....

    mgaeckler schrieb:

    fr33g schrieb:

    Heißt wenn ich jetzt den Zuweisungsoperator der Basis als virtual deklariere und mit einem Zeiger auf die Basis auf ein abgeleitetes Objekt zeige, dann ruft er die Basiszuweisung auf, da in der abgeleiteten Klasse kein virtual Zuweisungsoperator ist oder?

    Korrekt.

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

    Danke schonmal

    Gruß freeG



  • 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.


Anmelden zum Antworten