Virtueller Zuweisungsoperator



  • Hey Leute,
    in habe heute gelesen man sollte Zuweisungsoperatoren nie virtuell deklarieren.
    Ich verstehe leider jedoch nicht so ganz warum?
    Dort stand was, weil dann die abgeleitete Klasse automatisch einen Zuweisungsoperator hat, der als Paramter, eine Referenz auf den Basistyp erwartet und einen der ne Referenz auf die abgeleitete Klasse erwartet.
    Verstehe nicht so ganz das Problem?

    Kann mir das vll jemand erklären?

    Vielen Dank schonmal

    Gruß freeG



  • Hallo,

    der Zuweisungsoperator macht in der Regel nur Sinn, wenn der eine Parameter eine Referenz auf den gleichen Typ ist.

    class TYPE
    {
       ...
       TYPE &operator = ( const TYPE &source );
    }
    

    virtuelle Funktionen funktionieren aber nur dann, wenn die Funktionen exakt die gleichen Parameter erwarten. So etwas dürfte also nicht funktionieren:

    class TYPE
    {
       ...
       virtual TYPE &operator = ( const TYPE &source );
    }
    
    class DERIVED : public TYPE
    {
       ...
       virtual DERIVED &operator = ( const DERIVED &source );
    }
    
    int main( void )
    {
       TYPE *ptr = new TYPE;
       TYPE *ptr2 = new DERIVED;
       TYPE *ptr3 = new DERIVED;
    
       *ptr = *ptr2;
       *ptr2 = *ptr3;
    }
    

    Hinzu kommt, daß es noch Copyconstruktoren gibt, die nicht virtuell sind, und eventuell auch noch vom Compiler welche generiert werden, die auch nicht virtuell sind. Welcher soll denn dann benutzt werden?

    mfg Martin



  • Das Beispiel funktioniert bei mir aber 🙄
    Ist aber eg ja ok, ich kann ja die Funktion überschreiben, dann is sie halt nicht virtuell...oder net?
    Aber verstehe immer noch net was passiert wenn man den Zuweisungsoperator virtuell macht.
    In meinem Buch stand irgendwas von wegen der virtuelle ist dann auch in der abgeleiteten Klasse aber mit Basisparameter und es existiert der von der abgeleiteten Klasse generierte Zuweisungsoperator...
    Aber wo liegt das Problem?

    Gruß freeG



  • hi,
    ich habe hier mal 2 Zitate gefunden, ich zitiere mal:

    Eines ist von Nexus:

    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.

    Und eines von http://velociraptor.mni.fh-giessen.de/Programmierung/ProgII-htmldir/node9.html

    Ein Zuweisungsoperator kann zwar virtuell definiert werden, die Zuweisung kann aber trotzdem nicht polymorph sein, da polymorphe Funktionen immer die gleichen Argumenttypen haben müssen.



  • fr33g schrieb:

    Das Beispiel funktioniert bei mir aber 🙄
    Ist aber eg ja ok, ich kann ja die Funktion überschreiben, dann is sie halt nicht virtuell...oder net?

    Mag sein, daß der Code etwas macht. Aber macht er das, was der Programmierer erwartet?

    Was würdest Du jetzt erwarten, was der Code macht? Ist das dann sinnvoll? Was passiert, wenn Du noch 'nen Copyconstructor hast? Der Code wird ganz schnell unwartbar. Deshalb: lass es sein.

    mfg Martin



  • Also:

    class TYPE
    {
       ...
       virtual TYPE &operator = ( const TYPE &source );
    }
    
    class DERIVED : public TYPE
    {
       ...
       // weg virtual DERIVED &operator = ( const DERIVED &source );
    }
    
    int main( void )
    {
       TYPE *ptr = new TYPE;
       TYPE *ptr2 = new DERIVED;
       TYPE *ptr3 = new DERIVED;
    
       *ptr = *ptr2;
       *ptr2 = *ptr3;
    }
    

    So wenn ich das jetzt kompiliere kommt kein Fehler.
    So was erwarte ich was er macht, beim ersten ruft er den Zuweisungsoperator von TYPE auf, beim zweiten vermute ich den compiliergenerierten Zuweisungsoperator, dieser ruft dann noch den von TYPE auf.
    Definiere ich in Derived selber einen Zuweisungsoperator, dann wird dieser aufgerufen, dieser ruft dann den von TYPE auf.

    Also ich verstehe nicht wirklich was das Problem ist wenn man ihn virtuell macht, aber genau das würde ich gern verstehen.

    EDIT:
    Oh ich hatte nen Fehler bei mir, ich hatte nicht Zeiger auf TYPE gemacht sondern auf Derived, und dann ist ja klar dass er den richtigen aufruft.
    Also hab das jetzt auf Zeiger auf TYPE geändert.
    So jetzt ruft er wenn ich einen Zuweisungsoperator für Derived definiere, egal ob der in TYPE virtual ist oder nicht nur den in TYPE auf, sprich der Teil von Derived ist uninitialisiert richtig?
    Ist das dann auch so wenn ich keinen Zuweisungsoperator definiere, sondern der Compiler einen erstellt?

    Das bedeutet also das Polymorphie für die Zuweisungsoperatoren nicht gilt, richtig?

    Also egal ob ich den Zuweisungsoperator von TYPE virtual mache oder nicht, dieser ist nicht für DERIVED verfügbar, da er nicht vererbt wird.
    DERIVED enthält dann also nur den compilergenerierten oder selbst erstellten Zuweisungsoperator, dieser wird jedoch niee aufgerufen wenn es ein Zeiger auf TYPE ist, richtig?

    Dann hätt ichs verstanden:D

    Danke schonmal

    Gruß freeG



  • #include <stdio.h>
    
    class base
    {
    	public:
    	int baseMem;
    
    	base( int n )
    	{
    		baseMem = n;
    	}
    	virtual base &operator = ( const base &src );
    };
    
    class derived : public base
    {
    	public:
    	int derivedMem;
    
    	derived( int n, int d ) : base( n )
    	{
    		derivedMem = d;
    	}
    	base &operator = ( const base &src );
    };
    
    base &base::operator = ( const base &src )
    {
    	puts( "base &base::operator = ( const base &src )\n" );
    	this->baseMem = src.baseMem;
    
    	return *this;
    }
    
    base &derived::operator = ( const base &src )
    {
    	puts( "base &derived::operator = ( const base &src )\n" );
    	this->baseMem = src.baseMem;
    
    	return *this;
    }
    
    int main( void )
    {
    	base *ptr1 = new base(1);
    	base *ptr2 = new base(2);
    
    	base *ptr3 = new derived(3,4);
    	base *ptr4 = new derived(5,6);
    
    	*ptr1 = *ptr2;
    	*ptr1 = *ptr3;
    	*ptr3 = *ptr1;
    	*ptr3 = *ptr4;
    
    }
    

    Ob das sinnvoll ist, sei mal dahin gestellt.

    mfg Martin



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

    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!?

    Aber ich bin doch der Annahme richtig, dass der Zuweisungsoperator nicht vererbt wird oder?
    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?

    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?

    Danke schonmal

    Gruß freeG



  • 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


Log in to reply