Vererbung von operator=



  • Hallo!

    Ich habe eine Klasse, die den Zuweisungsoperator und den Kopierkonstruktor überschreibt. Von dieser leite ich eine weitere Klasse ab. Jetzt möchte ich einer Instanz der abgeleiteten mit "=" eine Instanz der Oberklasse zuweisen. Das mag der Compiler leider nicht. Im Code sieht das vereinfacht so aus:

    class mutter {
    
      operator=( mutter ob ) {
    
        daten = ob.daten;
      }
    };
    
    class kind : public mutter {
    };
    
    mutter m;
    kind k;
    
    k = m; // <- hier hackts
    

    Hat einer ne Idee, wieso das nicht geht? Werden die Operatoren nicht mit vererbt?



  • Nein, operator= wird nicht vererbt (dazu ist er zu elementar), sondern für jede Klasse im Notfall neu erstellt.

    Und der op= der abgeleiteten Klasse ruft erst den operator der Basisklasse auf und kopiert dann die Elemente, die neu dazugekommen sind. Allerdings willst du deinem kind-Objekt ein mutter-Objekt zuweisen - und da hat der Compiler keine Ahnung, woher er die Werte für die kind-Member nehmen soll.

    (btw, wenn sich der op= auf ein "daten=ob.daten" reduziert, kannst du auch den Default-Operator dafür verwenden)



  • Ja, das Beispiel ist etwas zu simple. Der Operator macht mehr als einfach nur Daten zu kopieren und muss daher überschrieben werden.

    Ich dacht halt, wenn ich kind=mutter machen, dann wird automatisch der Operator der Mutter genommen.



  • Nein, wird er nicht - das geht nur in der anderen Richtung m=k; (oder wenn du explizit einen operator= definierst).

    PS: Es ist zwar nicht wirklich vom Standard vorgeschrieben, aber üblicherweise sieht ein op= so aus:

    class Typ
    {
    public:
      Typ& operator=(const Typ& other)
      {
        //eigentliche Zuweisung
        //achte auf mögliche Selbstzuweisungen
        return *this;
      }
    }
    

    (auf diese Weise kannst du auch Zuweisungsketten ( a=b=c=somedata; ) verwenden und du sparst dir eine Kopier-Operation)



  • Okay, danke! Wieder was gelernt... Ich hab nun allen abgeleiteten Klassen ihren eigenen Operator gegeben und alles ist gut.



  • Denkst du zumindest *scnr*

    Für den Moment mag ja der Compiler glücklich sein und dein Programm funktionieren. Aber ob dein Programm wirklich das macht, was es soll, steht auf einem anderen Blatt.

    Beispiel gefällig:

    class base
    {
    public:
      base& operator=(const base&);
    ...
    };
    
    class kind1 : public base
    {
      int m_value;
    public:
      kind1& operator=(const base& other)
      {
        base::operator=(other);// base-Anteil kopieren
        m_value = 0;           // eigene Anteile nicht verfügbar - Defaultwert verwenden
        return *this;
      }
    };
    
    class kind2 : public base {/* analog aufgebaut */};
    
    int main()
    {
      kind1 k1;
      kind2 k2;
      k2=k1;
    }
    

    Normalerweise sollte die letzte Anweisung scheitern, aber durch den eigenen operator= funktioniert das (k1 wird in ein base-Objekt umgewandelt und zugewiesen) - jetzt mußt du überlegen, ob das der Sinn der Sache ist.

    PS: Kannst du mal grob umreißen, was du vorhast? Womöglich hat du ja schon im Ansatz des Klassendesigns einen Denkfehler eingebaut.



  • littlejohn schrieb:

    ..
    <- hier hackts

    Wer hackt ?
    "....
    I cut down trees, I eat my lunch,
    I go to the lavat'ry.
    On Wednesdays I go shopping
    And have buttered scones for tea.
    ..."

    Gruß,

    Simon2.



  • CStoll schrieb:

    Nein, wird er nicht - das geht nur in der anderen Richtung m=k; ...

    ... und das deswegen, weil so kein "halbdefiniertes Ergebnis" zurückbleibt.
    (Das Argument, dass der Mutter::operator=() nicht die zusätzlichen Kind-Attribute füllen kann, bleibt einfach bestehen).
    Beispiel (nur für diesen Zusammenhang, nicht für "perfekten Code")

    class Mutter {
    protected:
        int a;
    public:
        virtual void seta(int i) { a = i; }
    ....
    }
    
    class Kind : public Mutter {
        int b;
    public:
        virtual void seta(int i) { Mutter::seta(i); b = 4*i; }
    ...
    };
    

    Wenn jetzt nur "die Mutterteile" (Mutter::a -> Kind::Mutter::a) kopiert würden, hätte man in einem Kind a und b inkonsistent....
    Dagegen kann man durchaus konsistent Kind::Mutter::a auf Mutter::a kopieren.

    Gruß,

    Simon2.



  • Also was ich mir da gedacht hab ist folgendes:

    Ich hab einen Interpreter und der hantiert mit einer Klasse namens "Union" herum. Diese hat Funktionen und Daten. Wenn der Interpreter fertig ist mit abarbeiten gibt er das Ergebnis der letzten Operation aus und zwar in Form einer solchen "Union". Damit diese von aussen besser handhabbar wird, leite ich davon Klassen ab, z.b. "UnionTools". Diese Klasse hat noch ein paar Funktionen, die innerhalb der Interpreters nicht benutzt/gebraucht werden.

    das sieht dann ungefähr so aus:

    Union Interpret() {...}
    
    UnionTools o = Interpret( "..." );
    

    Die Union enthält nicht nur integer oder strings sondern kann auch weitere Unions enthalten. Daher wollte ich da noch ein paar Funktionen ranbauen um besser mit dem Ergebniss umgehen zu können.

    Ist die Idee schlecht? Wenn ja, was wäre ein besserer Ansatz?



  • und jetzt wirds richtig kompliziert. Das mit den operatoren funktioniert soweit wunderbar, aber der Kopierkonstruktor macht nicht was er soll:

    Das ist die Klasse:

    /**
     * copy contructor
     */
    Chess44Man::Chess44Man( const CMScriptDataUnion &o ) {
    
    	CMScriptDataUnion::CMScriptDataUnion( o );
    }
    
    void Chess44Man::operator=( CMScriptDataUnion o ) {
    
    	CMScriptDataUnion::operator=( o );
    }
    

    und das die Zuweisung:

    CMScriptDataUnion oUnion( 100 );
    
    	Chess44Man oMan = oUnion;  //Variante I: wird ausgeführt, bringt aber nichts
    
    	oMan = oUnion;  //Variante II: wird korrekt ausgeführt
    

    Also Variante I wird korrekt ausgeführt und wenn ich mit dem Debugger reingucke wird auch der Code der Kopierkonstruktors ausgeführt. Danach ist "oMan" allerdings "leer", also die zugewiesenen Daten sind wieder vergessen.

    Bei Variante II ist hinterher alles so wie es soll.

    Woran liegt das?



  • Wie Scott Meyers sagt: Mache Basisklassen abstrakt!



  • Danke!


Anmelden zum Antworten