Undefined Reference auf eine abstrakte Funktion?



  • Ich stehe momentan ziemlich auf dem Schlauch, denn ich habe zuvor bereits mit virtuellen Zuweisungsoperatoren gearbeitet, doch diesmal gelingt es mir einfach nicht... In folgendem Beispiel gibt der Compiler (gcc unter Code::Blocks) die Meldung aus, dass keine Referenz auf Array::operator= gefunden werden konnte (wie auch, ist ja abstrakt!). Der fast identisch aussehende Operator == geht komischer Weise.

    class Array
    {
        public:
        virtual Array& operator = ( const Array& a ) = 0;
        virtual bool   operator ==( const Array& a ) = 0;
    };
    
    class PolymArray : public Array
    {
        public:
        Array& operator = ( const Array& a ) { return *this; }
        bool   operator ==( const Array& a ) { return  true; }
    };
    
    int main()
    {
        PolymArray a;
        PolymArray b;
        a==b;   // Kein Problem.
        a=b;     // Ein Problem!
        return 0;
    }
    

    Was mache ich falsch?



  • class PolymArray : public Array
    {
        public:
        PolymArray& operator = ( const PolymArray& a ) { return *this; }
        bool   operator ==( const Array& a ) { return  true; }
    };
    


  • drakon schrieb:

    class PolymArray : public Array
    {
        public:
        PolymArray& operator = ( const PolymArray& a ) { return *this; }
        bool   operator ==( const Array& a ) { return  true; }
    };
    

    Ist das nicht einfach nur eine Überladung statt Polymorphie?
    Mal ganz nebenbei täte mich auch interessieren, warum es bei == geht und bei = nicht.



  • Richtig, drakons Version von PolymArray::operator= weist einen anderen Parameter auf als in Array::operator=. Nur beim Rückgabetyp greift das Prinzip der Kovarianz, auf das drakon wahrscheinlich hinauswollte. Wenn ich seine Version von PolymArray::operator= einsetze, wird Array::operator= also nicht redefiniert, was der Compiler auch bestätigt:

    ||=== Testprogramm, Debug ===|
    In function int main()': error: cannot declare variable \a' to be of type `PolymArray'
    error: because the following virtual functions are abstract:
    error: virtual Array& Array::operator=(const Array&)
    error: cannot declare variable `b' to be of type `PolymArray'
    error: since type `PolymArray' has abstract virtual functions

    Gibt es noch weitere Ideen?



  • Ja, die Basisklasse muss den operator= ebenfalls implementieren. Der Aufruf von operator ruft alle operator= -Funktionen seiner Elternklassen auf. Dabei ist es ganz gleich, ob dies abstrakte Klassen sind, und die operator= -Funktion rein virtuell ist.
    Das ist analog zur nicht virtuellen operator= -Funktion.

    //so (diese Variante ist zu bevorzugen):
    class Array
    {
        public:
        virtual Array& operator = ( const Array& a ) = 0 { return *this; }
        virtual bool   operator ==( const Array& a ) = 0;
    };
    //oder so:
    class Array
    {
        public:
        virtual Array& operator = ( const Array& a ){ return *this; }
        virtual bool   operator ==( const Array& a ) = 0;
    };
    


  • Tachyon schrieb:

    Der Aufruf von operator ruft alle operator= -Funktionen seiner Elternklassen auf.

    Nur wenn er automatisch generiert wird. Bei selbstgeschriebenen op= muss man das falls benötigt selber machen.

    Ich persöhnlich frage mich allerdings was ein polymorpher op= bewerkstelligen soll. Folgendes wäre z.B. sinnfrei:

    class Fahrzeug;
    class Auto : public Fahrzeug {/*...*/}
    class Zweirad : public Fahrzeug {/*...*/}
    
    //...
    
    karre = mopped; //????
    


  • pumuckl schrieb:

    Tachyon schrieb:

    Der Aufruf von operator ruft alle operator= -Funktionen seiner Elternklassen auf.

    Nur wenn er automatisch generiert wird. Bei selbstgeschriebenen op= muss man das falls benötigt selber machen.

    Jap. Für PolymArray wird auch automatisch ein Default- operator=() generiert. Nämlich PolymArray & operator=(PolymArray const &) . Und dieser versucht, den operator= von Array aufzurufen, was folgerichtig zu einer undefinierten Referenz führt.
    Erzwingt man die Benutzung des korrekten Operatos mit a = static_cast<Array&>(b) , dann funktioniert es wie erwartet.
    Das gleiche hast Du auch beim nicht-virtuellen operator= :

    #include <iostream>
    
    struct test
    {
        test& operator=(char c){ std::cout << "char\n"; return *this; }
    };
    
    int main()
    {
        test a;
        test b;
    
        a = 'c'; //ruft test& operator=(char c) auf
    
        b = a; //ruft default operator=() auf
    }
    

    PS: Du hast aber damit recht, dass der rein virtuelle Zuweiser wenig Sinn macht. In den seltensten Fällen tut er das, was man will.



  • Tachyon schrieb:

    PS: Du hast aber damit recht, dass der rein virtuelle Zuweiser wenig Sinn macht. In den seltensten Fällen tut er das, was man will.

    Das gilt aber auch für operator== .

    Wenn man wirklich Objekte unterschiedlichen Typs vergleichen möchte (was ich an sich schon fragwürdig finde), wäre vielleicht etwas über dynamic_cast möglich. Damit mindestens false zurückgegeben werden kann, wenn die dynamischen Typen unterschiedlich sind.

    Das Problem bei virtuellen Funktionen ist auch die Asymmetrie; a == b kann unter Umständen etwas anderes bewirken als b == a .



  • Danke, euer Tipp war goldrichtig! Der standardmäßig zur Verfügung gestellte Zuweisungsoperator von PolymArray bedurfte des alten, allerdings abstrakten Zuweisungsoperators.

    Was ich mit einem abstrakten Zuweisungsoperator mache, kann ich euch gern sagen: Es handelt sich um eine abstrakte Basisklasse, die ihrerseits noch keine Elementdaten besitzt. Eine abstrakte Zuweisung ist da denkbar... zumindest jedoch nicht unsinnig.



  • koril-k schrieb:

    Was ich mit einem abstrakten Zuweisungsoperator mache, kann ich euch gern sagen: Es handelt sich um eine abstrakte Basisklasse, die ihrerseits noch keine Elementdaten besitzt. Eine abstrakte Zuweisung ist da denkbar... zumindest jedoch nicht unsinnig.

    Kannst Du mal ein Beispiel nennen, wieso das sinnvoll sein soll?



  • Tachyon schrieb:

    Kannst Du mal ein Beispiel nennen, wieso das sinnvoll sein soll?

    damit kann ich endlich typen ändern. schau:

    Sparkonto k1;
    Girokonto k2;
    k2=k1;//wird automagisch zum Sparkonto
    


  • volkard schrieb:

    Tachyon schrieb:

    Kannst Du mal ein Beispiel nennen, wieso das sinnvoll sein soll?

    damit kann ich endlich typen ändern. schau:

    Sparkonto k1;
    Girokonto k2;
    k2=k1;//wird automagisch zum Sparkonto
    

    😮



  • volkard schrieb:

    schau:

    Nettes Beispiel. 😉



  • Meine Anwendung gebe ich euch gern. Ich habe eine abstrakte Basisklasse namens "Array", jedoch ist die interne Darstellung de rDaten in den unterschiedlichen benötigten Arrays sehr verschieden. Konkret habe ich zwei Arrays: PolymArray speichert die Daten mittels Zeiger damit ich Polymorphie nutzen kann und DirecTArray speichert die Daten direkt ab wie in int a[10]. Wenn ich eine Referenz auf ein Array habe, z.B. Array& a = b wobei b z.B. ein PolymArray ist, dann soll sowohl a = b als auch a = c funktionieren, wobei c ein DirectArray ist, oder noch konkreter: a = x wobei x entweder DirectArray, PolymArray oder Referenz vom Typ Array ist. Die Zuweisung übernimmt die Daten des angegebenen Arrays und legt sie in der eigenen Datenstruktur ab. Das gelingt dem Operator deshalb, weil alle Arrays über einen gemeinsamen Datenzugriff verfügen, der in der abstrakten Klasse verlangt wird und die Darstellung der Daten nach außen hin verbirgt; nämlich Array::operator[].


Anmelden zum Antworten