Basisklasse als reine Vorlage



  • Hallo,
    ich kann mir vorstellen das die Frage nicht ganz neu ist, habe
    aber auch nach etwas Suchen keine verständliche Antwort gefunden.

    Ich möchte Basisklassen verwenden, die nur die notwendigen
    Eigenschaften und Methoden an die Abgeleiteten Klassen vererben.
    Dort möchte ich dann die konkreten Implementationen der
    Funktionen definieren.

    Doofes Beispiel:

    class Fahrzeug
    {
        public:
            int Farbe;
            void Fahre(void);
    };
    
    class Auto : public Fahrzeug
    {
       // egal
    };
    

    Wenn ich das so mache, muss ich die Funktion Fahre() in der Klasse
    Fahrzeug erneut Deklarieren und Definieren.
    Ich möchte Sie dort aber nur Definieren (müssen).
    (Die Variable Farbe ist aber in Auto schon vorhanden.)
    Fahrzeug habe ich nur in einer Include Datei definiert.
    Ich habe auch schon von virtual und abstract gehört.
    Das Problem ist, das dann immer die vtable bzw. eine späte Bindung
    ins Spiel kommt.
    Da ich auf einem Embedded System mit wenig Speicher arbeite
    möchte ich aber eigentlich alles zur Kompilierzeit festlegen.
    Und das sollte doch hier auch gehen oder?
    (Geht ja auch, nur das jetzt die Basisklasse eigentlich sinnlos ist
    wenn ich eh alles neu deklarieren muss)

    Danke!



  • Hallo,

    versuchs so

    class Fahrzeug
    {
        public:
            int Farbe;
            void Fahre(void) = 0;
    };
    

    Gruß Simon



  • varginator schrieb:

    Hallo,

    versuchs so

    void Fahre(void) = 0;
    

    "=0" nur für virtual member function:

    virtual void Fahre(void) = 0;
    

    rekisum schrieb:

    (Geht ja auch, nur das jetzt die Basisklasse eigentlich sinnlos ist wenn ich eh alles neu deklarieren muss)

    "wieder deklarieren" nur das, was wird "neu definieren".



  • Also das mit virtual void funktioniert schon.
    Musste nur die Funktion _cxa_pure_virtual() hinzufügen.

    wieder deklarieren" nur das, was wird "neu definieren"

    Damit meinst du das ich die Funktion erneut deklarieren muss
    wenn ich sie in der abgeleiteten Klasse definiere?

    Aus einem anderen Forum:

    Using a templated class (as opposed to virtual functions) happens at
    compile-time, not runtime, so you get no performance hit at all, just
    cleaner code, and type-checking.

    Heisst das, dass ich vielleicht lieber mit Templates arbeiten sollte
    statt mit Virtuellen Funktionen wie von dir vorgeschlagen?
    😕



  • rekisum schrieb:

    Damit meinst du das ich die Funktion erneut deklarieren muss
    wenn ich sie in der abgeleiteten Klasse definiere?

    Ja. Der Compiler muss beim Lesen der Klasse schliesslich wissen, welche Methoden angeboten werden. Wenn nichts steht, geht er davon aus, dass es sich um die Methode der Basisklasse handelt.

    Heisst das, dass ich vielleicht lieber mit Templates arbeiten sollte
    statt mit Virtuellen Funktionen wie von dir vorgeschlagen?
    😕

    Nein, Templates sind ein ganz anderer Abstraktionsmechanismus - Geschwindigkeit ist normalerweise nicht der ausschlaggebende Punkt für sie.

    Du kannst Vererbung ohne Polymorphie nutzen. Virtuelle Funktionen bringen dir schliesslich nur was, wenn du Instanzen von abgeleiteten Klassen über einen Zeiger oder eine Referenz auf eine Basisklasse aufrufen möchtest. Brauchst du das? Dann nutze Polymorphie.



  • Du kannst Vererbung ohne Polymorphie nutzen.

    Äh, ok wie ginge denn das?


  • Mod

    rekisum schrieb:

    Du kannst Vererbung ohne Polymorphie nutzen.

    Äh, ok wie ginge denn das?

    Indem du einfach nur vererbst, aber keine Basisklassenzeiger benutzt. Vererbung ist für mehr Dinge nützlich als nur Polymorphie.



  • rekisum schrieb:

    Du kannst Vererbung ohne Polymorphie nutzen.

    Äh, ok wie ginge denn das?

    static inheritance. Die wenigstens Sprachen unterstützen das direkt 😞
    Muss dann meistens über public inheritance gemacht werden und das hat dann viele fallstricke.

    Du hast zB einen value typen wie std::list. Nun willst du aber keinen counter für die anzahl der objekte die sie beinhaltet anbieten. Dann schreibst du eine std::list implementierung ohne size() oder mit einem size() das O(n) komplexität hat.

    Dann stellst du fest du hättest gerne ein size() mit O(1) also erbst du statisch von std::list und bietest ein size() mit O(1) an indem du die objekte immer mitzählst.



  • @ rekisum
    Hm, war vielleicht etwas irreführend. Eine abstrakte Basisklasse mit rein virtuellen Funktionen scheint mir doch angebracht. Schliesslich legt der Umstand, dass der Name der Funktion in der Basisklasse bekannt sein muss, nahe, dass polymorphes Verhalten erwünscht ist. Falls es dir allerdings nur darum geht, Eigenschaften zu vererben und bestehende Typen zu erweitern (ein Auto ist ein spezialisiertes Fahrzeug), ist "reine" Vererbung wohl der bessere Weg. Dann braucht Fahrzeug aber auch keine Memberfunktion Fahre() .

    Allerdings kannst du selbst mit polymorphen Klassen Funktionsaufrufe statisch binden, die dann ebenso schnell sind, wie wenn niemals virtual vorkäme. Den kleinen VTable-Overhead müssen die Objekte jedoch auch mit sich mitschleppen, wenn du die Funktionalität nicht nutzt (aber trotzdem virtual -Funktionen hast) - also musst du dich entscheiden, in C++ hast du diese Möglichkeit. 🙂

    class Fahrzeug
    {
        public:
            virtual void Fahre() = 0;
            virtual ~Fahrzeug() {}
    };
    
    class Auto : public Fahrzeug
    {
        public:
            virtual void Fahre();
    };
    
    void Auto::Fahre()
    {
       // führe spezifische Anweisungen aus
    }
    
    int main()
    {
    	Auto MeinPeugeot;
    	MeinPeugeot.Fahre();
    }
    

    Bei diesem Aufruf von Fahre() ist aufgrund des statischen Typs von MyPeugeot klar, dass Auto::Fahre() aufgerufen werden muss.



  • Virtuelle Funktionen bringen dir schliesslich nur was, wenn du Instanzen von abgeleiteten Klassen über einen Zeiger oder eine Referenz auf eine Basisklasse aufrufen möchtest

    Naja, mit

    f1 = new Auto();
    f1->Fahre();
    

    habe ich ja eine Instanz einer abgeleiteten Klasse über einen Zeiger aufgerufen, oder?

    Das möchte ich wahrscheinlich schon machen.
    Ansonsten möchte ich aber nur Eigenschaften und Schnittstellen von
    Basisklassen vererben.
    Und das ganze am liebsten zur Kompilierzeit festgelegt.

    Was müsste man denn machen wenn man eine Implementation in einer
    abgeleiteten Klasse erzwingen will?
    Also um sicherzustellen das jedes Fahrzeug auch eine Funktion Fahre() hat!



  • rekisum schrieb:

    habe ich ja eine Instanz einer abgeleiteten Klasse über einen Zeiger aufgerufen, oder?

    Die Frage kann ich dir leider nicht beantworten, da du das Wesentliche - den Deklarationstyp von f1 - weggelassen hast. 😉
    Aber falls f1 vom Typ Fahrzeug* ist, dann ja.

    rekisum schrieb:

    Und das ganze am liebsten zur Kompilierzeit festgelegt.

    Wie willst du etwas zur Kompilierzeit festlegen, das erst zur Laufzeit bekannt ist? Ich meine damit den dynamischen Typen, der sich hinter Basisklassenzeigern verstecken kann.

    rekisum schrieb:

    Was müsste man denn machen wenn man eine Implementation in einer
    abgeleiteten Klasse erzwingen will?
    Also um sicherzustellen das jedes Fahrzeug auch eine Funktion Fahre() hat!

    Eben: Eine rein virtuelle Funktion wie beschrieben hinzufügen. Solange abgeleitete Klassen diese nicht implementieren, bleiben sie ebenfalls abstrakt.



  • Etwas Offtopic @ Shade Of Mine:

    Shade Of Mine schrieb:

    static inheritance. Die wenigstens Sprachen unterstützen das direkt 😞
    Muss dann meistens über public inheritance gemacht werden und das hat dann viele fallstricke.

    Ich glaube, ich kann mir so etwa vorstellen, was du mit static inheritance meinst. Sehe ich das richtig, dass die entsprechende Vorgehensweise in C++ auf Aggregation und Neu-Implementierung der Methoden (also eine Wrapperklasse) basiert? Welche Sprachen unterstützen denn dieses Konzept direkt?


Anmelden zum Antworten