Operatoren in Basisklassen !?



  • Kann mir jemand erklären, warum der nachfolgende Code nicht funktioniert?

    Ich möchte eine abstrakte Basisklasse erstellen, die aufgrund ihrer Verwendung mehrere operator-Funktionen benötigt. Aber die Deklaration haut irgendwie nicht hin und ich verstehe nicht warum...

    class Base
    {
    public:
    	virtual friend bool operator<(const Base&, const Base&) = 0;
    };
    

    Danke im Voraus!



  • Du definierst den operator< als freie Funktion. Als solche kann sie nicht virtuell und erst recht nicht abstrakt sein.



  • Bashar schrieb:

    Du definierst den operator< als freie Funktion. Als solche kann sie nicht virtuell und erst recht nicht abstrakt sein.

    Ok. Und was ist eine freie Funktion? Und wie kann ich es besser machen?



  • Eine freie Funktion ist eine, die keine Memberfunktion einer Klasse ist. Du hast da ja nur eine friend -Deklaration einer Funktion, die irgendwo außerhalb der Klasse definiert sein muss.
    Was du besser machen kannst? Keine Ahnung, entweder machst du eine Memberfunktion daraus oder du machst sie nicht virtuell. Kommt drauf an, was du erreichen willst.



  • ichbineinnoob schrieb:

    Bashar schrieb:

    Du definierst den operator< als freie Funktion. Als solche kann sie nicht virtuell und erst recht nicht abstrakt sein.

    Ok. Und was ist eine freie Funktion? Und wie kann ich es besser machen?

    Frei ist global. Die bekannteste ist:

    int main()
    {
        return 0;
    }
    

    Was deinen Code angeht:

    class Base
    {
    public:
    	virtual bool operator<(const Base&)=0;
    };
    

    friend weg, den ersten Parameter weg (Methoden übernehmen automatisch einen Parameter auf das aufrufende Objekt, auch this -Zeiger genannt), und es sollte funktionieren.



  • Glühbirne schrieb:

    Was deinen Code angeht:

    class Base
    {
    public:
    	virtual bool operator<(const Base&)=0;
    };
    

    friend weg, den ersten Parameter weg (Methoden übernehmen automatisch einen Parameter auf das aufrufende Objekt, auch this -Zeiger genannt), und es sollte funktionieren.

    Das habe ich gerade auch versucht. Nun lautet der Fehler:

    C2259: Instanz von abstrakter Klasse kann nicht erstellt werden

    und das verstehe ich wieder nicht 😕 😕



  • Wenn du nicht weißt, was eine abstrakte Klasse ist, solltest du keine definieren.



  • Wo genau kommt denn der Fehler? Sicherlich nicht in dem Codestückchen. Versuchst du denn jetzt irgendwo ein Objekt dieser Klasse zu instanzieren?

    Lg freeG



  • fr33g schrieb:

    Wo genau kommt denn der Fehler? Sicherlich nicht in dem Codestückchen. Versuchst du denn jetzt irgendwo ein Objekt dieser Klasse zu instanzieren?

    Lg freeG

    Sorry mein vorheriger Text war falsch.

    Ich hatte diese Zeile noch stehen:

    map<pair<Base,Base>,Base>
    

    Ohne diese funktioniert es. (Sorry!)

    Kann man keine abstrakten Klassen in std::pair oder std::map benutzen?



  • fr33g schrieb:

    Wo genau kommt denn der Fehler?

    +1 für diese unnötige Frage.

    Sobald du eine Methode als pure virtual deklarierst, wird dessen Klasse abstrakt. Und abstrakte Klassen können nicht instanziert werden.

    Lern zuerst was abstrakte Klassen + Polymorphie ist und dann wende es an. Programmieren ist nicht Chemie.



  • ichbineinnoob schrieb:

    Kann man keine abstrakten Klassen in std::pair oder std::map benutzen?

    Man kann eine abstrakte Klasse nie instanzieren. Es würde ja auch keinen Sinn ergeben. Und Container instanzieren nun mal (klar, oder?). Und wieso sollte man einen Vergleichsoperator rein virtuell machen? Was ergibt das für einen Sinn?



  • Vergleichsoperatoren als Memberfunktionen sind (meistens) ungünstig, weil keine impliziten Umwandlungen der linken Seite vorgenommen werden. Etwa wird

    class zahl {
    public:
      zahl(int val) : val_(val) { }
    
      virtual bool operator<(zahl const &rhs) {
        return val_ < rhs.val_;
      }
    
    private:
      int val_;
    };
    
    ...
    
    zahl z = 2;
    if(1 < z) { // Compilerfehler!
    }
    

    nicht funktionieren. Das lässt wie folgt umgehen:

    class zahl {
    public:
      zahl(int val) : val_(val) { }
    
      virtual bool compare(zahl const &rhs) {
        return val_ < rhs.val_;
      }
    
    private:
      int val_;
    };
    
    bool operator<(zahl const &lhs, zahl const &rhs) {
      return lhs.compare(rhs);
    }
    
    ...
    
    zahl z = 2;
    if(1 < z) { // kein Problem.
    }
    

    Was es natürlich nicht löst, ist die Problematik doppelt virtueller Funktionen (wenn die Funktionsauswahl anhand der Laufzeittypen beider Parameter erfolgen soll).



  • Auch wenn ich nicht der Erste bin ...

    Warum möchtst du eine abstrakte Klasse erstellen, wenn du vorhast, diese zu instanzieren? Das ist Blödsinn. Aus gutem Grund ist das nicht möglich, die Funktionen besitzen ja keinen Code - das hast du mit der Zuweisung von 0 ja auch deutlich gemacht. Ein Objekt ist so nicht verwendbar.

    Weißt du überhaupt, was pure virtual bedeutet? Wenn nicht, weißt du dann, was virtual bedeutet?

    PS: Wenn du das ganze als Konstukt mit Basiszeigern auf abgeleitete Objekte einrichten willst, dann verwende Zeiger. Diese benötigen keine Instanzen, weshalb Vorwärtsdeklarationen auch möglich sind:

    class AbstractBaseClass
    {
    public:
        //Meine Funktionen.
        virtual bool Foo()=0;
        virtual int Bar(int&,int*)=0;
    }
    
    int main()
    {
        //Das funktioniert nicht:
        AbstractBaseClass Object;
    
        //Das schon:
        AbstractBaseClass*Pointer;
    }
    

    Aber der Zeiger wird nie auf ein reines AbstractBaseClass -Objekt zeigen, sondern immer auf Objekte abgeleiteter Klassen. Das Konzept nennt sich "Polymorphie".



  • KeinExperementieren schrieb:

    fr33g schrieb:

    Wo genau kommt denn der Fehler?

    +1 für diese unnötige Frage.

    Wieso unnötig? Ist mir schon klar dass er versucht hat eine Instanz des ADT zu erstellen, das sollte aber eg aus meinem Post hervorgehen. Ich wollte damit bezwecken, dass er den Codeteil zeigt, wo das vorkommt, damit man ihm dann helfen kann und erklären kann warum das nicht geht und was er vll ändern muss oder ob sein Design komplett falsch ist( was normalerweise der Fall ist, wenn man versucht einen ADT zu instanzieren ).

    Lg freeG



  • Allen Antwortern und Lesern zunächst einmal vielen Dank!

    Auch wenn es nicht so scheint (was an dieser eigensinnigen Programmiersprache liegen muss^^), ich kenne die Grundlagen der objektorientierten Programmierung.
    Ich habe lediglich versucht eine std::map zu erstellen, die mehrere Objekte einer abgeleiteten Klasse speichern kann (also Bsp: map<Auto,Auto>, wobei Auto eine abstrakte Basisklasse ist, später speicher ich dann Objekte der Klasse Renault oder Opel in der std::map ab (ein besseres Beispiel fällt mir gerade nicht ein)). Die Basisklasse wiederum sollte quasi ein Interface sein. Das Problem ist nun, dass die std::map die Objekte, die man abspeichern möchte, scheinbar instanziiert (korrigiert mich, wenn ich falsch liege) und das wusste ich einfach nicht (dachte das passiert erst später). Beim kompilieren hat mir Visual 2008 Express dann immer gesagt, dass ich diese und jene Methoden deklarieren (oder war es definieren?) muss, damit die std::map meine Objekte überhaupt speichern kann. Das war/ist mein Problem...

    EOutOfResources schrieb:

    Man kann eine abstrakte Klasse nie instanzieren. Es würde ja auch keinen Sinn ergeben. Und Container instanzieren nun mal (klar, oder?). Und wieso sollte man einen Vergleichsoperator rein virtuell machen? Was ergibt das für einen Sinn?

    Jetzt ist es mir klar. Danke! Die Vergleichsoperatoren sollte ich nach Compilermeldung halt implementieren, damit ich die Klassen überhaupt in der std::map speichern kann.

    Ich glaube ich mache doch besser eine nicht abstrakte Basisklasse, wenn es nicht anders geht. Auch wenn das total blöd aussieht.



  • ichbineinnoob schrieb:

    Auch wenn es nicht so scheint (was an dieser eigensinnigen Programmiersprache liegen muss^^), ich kenne die Grundlagen der objektorientierten Programmierung.
    Ich habe lediglich versucht eine std::map zu erstellen, die mehrere Objekte einer abgeleiteten Klasse speichern kann (also Bsp: map<Auto,Auto>, wobei Auto eine abstrakte Basisklasse ist, später speicher ich dann Objekte der Klasse Renault oder Opel in der std::map ab (ein besseres Beispiel fällt mir gerade nicht ein)).

    Das funktioniert aber nicht. Die map speichert genau das, was du deklariert hast, also Instanzen von Auto, und nichts anderes.

    Die Basisklasse wiederum sollte quasi ein Interface sein. Das Problem ist nun, dass die std::map die Objekte, die man abspeichern möchte, scheinbar instanziiert (korrigiert mich, wenn ich falsch liege) und das wusste ich einfach nicht (dachte das passiert erst später).

    Der Code, der die Objekte instanziiert, muss ja erzeugt werden, ob du ihn nun später mal aufrufst oder nicht. Du würdest das dann ungefähr so tun (BTW warum ist eigentlich key und value beides Auto?):

    Renault a, b;
    meineMap[a] = b;
    

    Die map kann wohl schlecht die Objekte a und b selbst speichern, sie speichert Kopien davon, und um Kopien anzulegen muss man Objekte instanziieren. Halten wir fest: map<Auto, Auto> muss in der Lage sein, Auto -Instanzen zu erstellen. Und das funktioniert nicht mit abstrakten Klassen.

    Zweites Problem, eigentlich viel wichtiger: Angenommen, du würdest die Klasse jetzt konkret machen. Dann würde der Code compilieren, aber wenn du einen Renault einfügst, ist noch lange kein Renault drin, sondern ein Auto. Das Problem nennt sich Slicing und ist eigentlich ganz einfach zu verstehen, wenn man sich mal modellhaft folgenden Code anguckt:

    Renault a;
    Auto b(a);
    

    Welchen Typ hat b? Auto natürlich. Der Kopierkonstruktor von Auto wird aufgerufen und kopiert sich nur die Teile heraus, die auch in Auto enthalten sind, alles was es in Renault noch zusätzlich gibt, fällt unter den Tisch.

    Wenn du mit Polymorphie willst, musst du mit Zeigern oder Referenzen arbeiten.



  • @Bashar: dank dir für die ausführliche Erklärung! 👍

    (Das mit Auto war nur ein Beispiel um mal zu erläutern was ich prinzipiell meinte).


Anmelden zum Antworten