Interfaces und abstrakte Klassen in C++



  • Damit ich es richtig versteh:

    Ein Interface in Java ist in C++: Eine Klasse, die nur Methodenrümpfe hat, die alle den Modifier virtual haben. Keine Implementierung auch nur einer Methode wird dabei vorgenommen.

    Eine abstrakte Klasse in Java ist in C++: Eine Klasse, die nur Methodenrümpfe hat, von der mindestens ein den Modifier virtual hat. Mindestens eine Methode wird implementiert.
    Im Gegensatz zu Java können dann abstrakte Klassen in C++ instanziiert werden.

    Kann man das so sagen?

    Tut mir leid wegen den Vergleichen, aber ich suche nach einer für mich verständlichen und korrekten Definition. 😃



  • Eine abstrakte Klasse in C++:

    class IVogel
    {
       virtual bool fly() = 0;
    };
    

    Eine Instanzierung von IVogel ist somit nicht möglich. JEde ableitende Klasse muss bool fly() implementieren.
    edit:
    NUR virtual reicht nicht, da virtual lediglich einen Virtual File Table Pointer anlegt, der es ermöglich die Methode zu überschreiben in einer abgeleiteten KLasse. Das "=0" bewirkt, dass fly() nicht angelegt wird, sondern auf NULL zeigt. Und damit ist die Klasse ungültig. Mit meinen Laienhaften Worten erklärt.
    HTH
    rya.



  • Klassen die nur eine einzige pur-virtuelle Methode enthalten können in C++ nicht instantiiert werden.

    PS: Siehe Scorcher 😉


  • Administrator

    Fenixx schrieb:

    Eine nicht ganz so einfache Frage. Ich würde sagen, dass Java da schärfer trennt. Wenn ich zu Beginn noch nichts genaueres über die Implementation weiß -> Interfaces. Kann ich schon Teile implementieren, aber noch nicht alles -> abstrakte Klassen.

    Nein, Java probiert so die Mehrfachvererbung einfach zu implementieren. Ich habe einige Zweifel, dass es den Java-Leuten ursprünglich um eine schärfere Trennung ging. Denn schlussendlich ist die Trennung nicht scharf, sondern eher mühsam.

    In C++ stellt jede Klasse eine Schnittstelle dar. Und das ist ja auch das Normalste, da jede Klasse eine Schnittstelle hat. Deshalb gibt es in C++ keine solche Trennung, da sie eigentlich gar nicht existiert.

    Fenixx schrieb:

    Wie sähe dann ein Java-Interface in C++ aus? Blauäugig würde ich sagen, dass ne Header-Datei dem schon sehr nahe kommt. Ist das vielleicht solch ein Lösungsansatz?

    Eine Header-Datei hat nichts mit Interface oder nicht Interface zu tun. Wenn du in C++ ein Java-Interface haben willst, dann mach alle Funktionen in der Klasse "pure virtual".

    Fenixx schrieb:

    In C++ kann man doch eine abstrakte Klasse instanziieren oder?
    Folgendes hat nämlich funktioniert:
    ...

    Abstrakte Klassen können nicht instanziiert werden. Dein Beispiel ist keine abstrakte Klasse.

    Grüssli



  • Dann verstehe ich folgendes nicht. Angenommen es liegt folgender Code vor:
    Die Definition:
    Vogel.h:

    #ifndef VOGEL_H_INCLUDED
    #define VOGEL_H_INCLUDED
    class Vogel
    {
    	public:
    		virtual void fliege();
    };
    #endif
    

    Die Implementation:
    Vogel.cpp:

    #include "Vogel.h"
    #include <iostream>
    void Vogel::fliege()
    {
    	printf("Der Vogel fliegt!");
    }
    

    Die Verwendung:

    int main()
        {
          Vogel vogel;
          vogel.fliege();
    }
    

    In diesem Fall lässt sich der Vogel instanziieren. Irgendwas verwirrt mich gerade



  • Noch ein Zusatz:
    Was möglich ist, ist einen Zeiger auf das Objekt zu haben. Allerdings muss der "Inhalt" des Zeigers die Implementation sein.
    Hier ein Beispiel für dich:

    // IVogel.hpp:
    class IVogel
    {
    public:
       virtual bool fly() = 0;
       virtual void cry() = 0;
    };
    
    // VogelImpl.hpp:
    class Chicken: public IVogel
    {
    public:
       virtual bool fly();
       virtual void cry();
    };
    
    // VogelImpl.cpp:
    
    bool Chicken::fly() 
    { return true; }
    
    void Chicken::cry()
    {  std::cout << "Pooock, Pock Pock!" << std::endl; }
    

    Das Problem ist nun die Instanzierung. Sowas kann man mit einer Factory-Klasse oder Funktion machen, ich nehm mal die Funktion, das ist weniger zu tippen :P.

    IVogel* ChickenFactory()
    {
      return new Chicken();
    }
    

    Somit weiss der User der API nichts von Chicken und verwendet nur IVogel. So eine Technik ist wunderbar für X-Platform Programming, da man so immer das gleiche Interface verwendet und Platformspezifische Member ausgeblendet werden.

    edit:
    Du hast dazwischengepostet 😃

    n diesem Fall lässt sich der Vogel instanziieren. Irgendwas verwirrt mich gerade

    Deine Klasse ist nicht abstrakt, lediglich der Member ist virtuell. Lies meinen Post bitte nochmal ganz :).
    rya.



  • class Vogel {
        virtual void fliege() = 0;
        virtual void lande() { /* default impl */ }
    };
    

    Vogel ließe sich nicht instantiieren.


  • Administrator

    Fenixx schrieb:

    In diesem Fall lässt sich der Vogel instanziieren. Irgendwas verwirrt mich gerade

    Die Klasse ist auch nicht abstrakt *sich wiederholt*
    Du solltest vielleicht nachschlagen was "pure virtual" heisst.

    class Vogel
    {
      virtual void fliege() = 0;
      // das = 0 bedeutet pure virtual. In der Klasse Vogel
      // existiert keine Definition von fliege.
    };
    

    Grüssli



  • Sorry, eure Texte konnte ich erst jetzt lesen.
    Ist dann IVogel eine Header- oder eine cpp-Datei? Rine cpp-Datei braucht doch eine Header-Datei oder?



  • Nein.
    Header-Dateien werden sozusagen cut&paste per #include in die Dateien eingefügt. Das ist alles... Sie werden in keinster weise speziell behandelt.



  • Fenixx schrieb:

    Sorry, eure Texte konnte ich erst jetzt lesen.
    Ist dann IVogel eine Header- oder eine cpp-Datei? Eine cpp-Datei braucht doch eine Header-Datei oder?

    *.hpp ist ein Header. Aber eine .cpp braucht nicht zwangsläufig einen Header. Allgemein definiert man halt die Funktionen aus einer .cpp in einer .hpp oder .h und man definiert Klassen in einer .hpp oder .h und implementiert sie in einer .cpp. Man kann eine Klasse aber auch "inline" in einer .hpp oder .h ausschreiben, sprich implementieren.
    Das mache ich bei kleinen Klassen, die nur versch. Member und Setter/Getter haben um einfache Daten zu kapseln. DAfür brauchts keine .cpp :).
    Beispiel:

    class MySimpleData
    {
    private:
       int a;
       float b;
    
    public: 
       int a() const { return a; }
       float b() const { return b; }
    
       setA(int _a) { a = _a; }
       setB(float _b) { b = _b; }
    };
    

    Das ist genauso erlaubt. Das ist nicht so strikt wie in Java.
    rya.



  • Es verwirrt mich nur deswegen, weil in meinen Augen Header-Dateien in gewisser Weise auch eine Klassendefinition, wie die eines Interfaces darstellen.
    Für IVogel habe ich demnach keine Header-Datei, sondern nur eine Cpp-Datei. Ist das so richtig?


  • Administrator

    @Fenixx,
    Wie kommst du jetzt auf *.cpp und *.hpp Dateien? Das hat rein gar nichts mit abstrakt, interface oder sonst was zu tun in diesem Zusammenhang.

    Ganz ehrlich, ich habe ein wenig das Gefühl, dass du den Schwierigkeitsgrad von C++ unterschätzt und denkst, dass du mit deinen Java-Kenntnissen ziemlich weit in C++ kommst. Dem ist aber definitiv nicht so!

    Ich glaube ich empfehle lieber ein Buch. Da du dich schon ein wenig im Programmieren auskennst, wäre wohl diese Lektüre für dich geeignet:
    Die C++-Programmiersprache | ISBN: 382731660X

    Grüssli



  • Fenixx schrieb:

    Es verwirrt mich nur deswegen, weil in meinen Augen Header-Dateien in gewisser Weise auch eine Klassendefinition, wie die eines Interfaces darstellen.
    Für IVogel habe ich demnach keine Header-Datei, sondern nur eine Cpp-Datei. Ist das so richtig?

    Du widersprichst dir selbst hehe.
    IVogel packt man natürlich in einen header. Denn man sollte keine .cpp includen, da das oft zu mehrfach definierten Symbolen führt und User der API verwirren würde.
    Also:
    Interfaces in eine .h/.hpp
    Implementation in eine .cpp
    rya.



  • Danke für eure Ausführungen.
    Jetzt hab ichs verstanden.

    Danke.



  • Zumal du nichtmal auf ".h" angewiesen bist. Du könntest die Datei auch .DasBraucheIchNochWoanders nennen 🙂 Ist halt nur länger 😉



  • Fenixx schrieb:

    Es verwirrt mich nur deswegen, weil in meinen Augen Header-Dateien in gewisser Weise auch eine Klassendefinition, wie die eines Interfaces darstellen.
    Für IVogel habe ich demnach keine Header-Datei, sondern nur eine Cpp-Datei. Ist das so richtig?

    Der Header sollte in der Regel (Ausnahmen sind u.a. Templates, aber das soll dich erst einmal nicht interessieren) die reine Deklaration enthalten, wenn man das mit Java vergleicht würde es am Beispiel einer rein abstrakten Basisklasse bedeuten, das es ähnlich wie eine Javaklasse ohne Methodenrümpfe aussieht (Oder wie ein Java Interface).

    // ivogel.h
    #ifndef IVOGEL_HEADER
    #define IVOGEL_HEADER
    
    class IVogel
    {
        public:
            void fliege() = 0;
    };
    
    #endif
    

    Sofern du keine Abstrakte Klasse betrachtest, sieht es ähnlich aus:

    // bar.h
    #ifndef BAR_HEADER
    #define BAR_HEADER
    
    class Bar
    {
        public:
            void foo();
    };
    
    #endif
    

    Die Implementierung der Methode erfolgt dann in der cpp-Datei:

    // bar.cpp
    #include "bar.h"
    
    //   <---> Dieser Teil steht für die Identifikation der Klasse
    void Bar::foo()
    {
    }
    

    Anmerkung: Destruktoren müssen, sofern man sie deklariert, immer eine Implementierung haben. Im Falle einer Basisklasse würde ich in der Regel einen virtuellen Destruktor zur Verfügung stellen. Häufig wird ein leerer Destruktor direkt in der Headerdatei definiert, und nicht extra dafür eine cpp-Datei erzeugt...

    // ivogel.h
    #ifndef IVOGEL_HEADER
    #define IVOGEL_HEADER
    
    class IVogel
    {
        public:
            virtual ~IVogel() {}; // virtuelle Destruktoren erfordern eine Implementierung
            void fliege() = 0;
    };
    
    #endif
    

    cu André



  • Decimad schrieb:

    Zumal du nichtmal auf ".h" angewiesen bist. Du könntest die Datei auch .DasBraucheIchNochWoanders nennen 🙂 Ist halt nur länger 😉

    😃
    oder .CPlusPlusInterfaceDefinition


Anmelden zum Antworten