Interface in C++?!



  • Hallo,

    ich komme aus der Welt des C# und beschäftige mich nun mit C++.

    Aus C# kenne ich Interfaces, mit denen ich verschiedenen Klassen die Deklaration von Methoden aufzwingen kann (z.B. sehr nützlich bei "Serialize" Funktionen oder "Copyable" Funktionen).

    Da ich "richtige" Interfaces in C++ nicht gefunden habe, habe ich diese eben als normale Klasse deklariert, was natürlich (wie ich jetzt bemerkt habe) nicht funktionieren kann. Die Klasse vererbt "kreuz und quer" und damit kommt es zu redefinitionen, was der Compiler nicht zulässt.

    Wie kann ich ein Interface in C++ realisieren?

    lg Max



  • Max64 schrieb:

    Da ich "richtige" Interfaces in C++ nicht gefunden habe, habe ich diese eben als normale Klasse deklariert, was natürlich (wie ich jetzt bemerkt habe) nicht funktionieren kann.

    Ähm doch, genau so funktionieren Interfaces in C++. Bitte Codebeispiele, die nicht funktionieren.

    mfg, René~



  • Max64 schrieb:

    Da ich "richtige" Interfaces in C++ nicht gefunden habe, habe ich diese eben als normale Klasse deklariert, was natürlich (wie ich jetzt bemerkt habe) nicht funktionieren kann...

    Warum sollte dies nicht funktionieren? Genau das ist der Weg in C++.
    Ein Interface ist nichts anderes als eine Klasse mit rein virtuellen Methoden.

    class IIrgendwas
    {
        private:
            // Entweder Destruktor privat machen, oder public und virtual...
            ~IIrgendwas() {}
        public:
            virtual void TueEtwas() = 0;
    };
    

    Nachtrag: Nur das in C++ Mehrfachvererbung auch mit Klassen möglich ist (und je nach Anwendungsfall kann auch die "virtuelle" Vererbung interessant sein, oder eine nicht-öffentliche Vererbung... das führt hier nur erst einmal zu weit).



  • asc schrieb:

    Entweder Destruktor privat machen, oder public und virtual (nicht rein virtual!).

    Warum nicht? Das ist der Standard-Weg, um eine ansonsten konkrete Klasse abstrakt zu machen:

    class Foo
    {
    public:
    	virtual ~Foo() = 0;
    };
    
    Foo::~Foo()
    {
    }
    
    class Bar : public Foo
    {
    };
    
    int main()
    {
    	Bar bar;
    }
    


  • Michael E. schrieb:

    asc schrieb:

    Entweder Destruktor privat machen, oder public und virtual (nicht rein virtual!).

    Warum nicht? Das ist der Standard-Weg, um eine ansonsten konkrete Klasse abstrakt zu machen:

    Hast recht, vergesse immer wieder, das einige Compiler an denen ich arbeite nicht Standardkonform sind (Und mit rein virtuellen Destruktoren Probleme machen - Selbst wenn man die Definition hinzufügt).



  • asc schrieb:

    Hast recht, vergesse immer wieder, das einige Compiler an denen ich arbeite nicht Standardkonform sind (Und mit rein virtuellen Destruktoren Probleme machen - Selbst wenn man die Definition hinzufügt).

    Welche Compiler sind das?



  • Max64 schrieb:

    ich komme aus der Welt des C# und beschäftige mich nun mit C++.

    Dann sei gewarnt. Einfach den C#-Stil nach C++ zu übertragen ist oft eine schlechte Idee. Ich schätze, dass Du -- genauso wie ich früher -- die Unterschiede dieser Sprachen unterschätzt. Ich habe auch zunächst mit abstrakten Klassen und dynamisch erzeugen Objekten losgelegt, so, wie ich es aus Java kannte. Heute sieht mein Programmierstil ganz anders aus. Aber neulich schrieb ich seit längerem mal wieder eine abstrakte Klasse und leitete davon ab. 😉 Es ist eben doch eine andere Sprache, in der man manchmal Dinge anders viel besser ausdrücken kann, wobei dieses "anders" in C# kein Äquivalent hat und erst neu kennengelernt werden muss...

    Max64 schrieb:

    Aus C# kenne ich Interfaces, mit denen ich verschiedenen Klassen die Deklaration von Methoden aufzwingen kann

    Diese Aufgabe übernehmen abstrakte Klassen in C++. Du solltest sie, wie schon hier erwähnt worden ist, mit einen virtuellen, öffentlichen oder nicht-virtuellen, geschützten Destruktor definieren:

    class abc
    {
    public:
      virtual ~abc() {}
      virtual int foo() const = 0;
      virtual void bar(int) = 0;
    };
    

    oder

    class abc
    {
    protected:
      ~abc() {}
    public:
      virtual int foo() const = 0;
      virtual void bar(int) = 0;
    };
    

    falls Du nicht vorhast, delete auf einen abc-Zeiger zu benutzen.

    Die "=0;"-Syntax deklariert sogenannte "pur virtuelle" Funktionen und machen die Klasse abstrakt.

    Gruß,
    kk



  • Max64 schrieb:

    Da ich "richtige" Interfaces in C++ nicht gefunden habe, habe ich diese eben als normale Klasse deklariert, was natürlich (wie ich jetzt bemerkt habe) nicht funktionieren kann. Die Klasse vererbt "kreuz und quer" und damit kommt es zu redefinitionen, was der Compiler nicht zulässt.

    Hallo Max64,

    google mal nach "include gurads".
    http://en.wikipedia.org/wiki/Include_guard

    Ich glaube genau das suchst du...

    In dem Zusammenhang werf ich dir einfach noch einen weiteren Begriff an den
    Kopf, den du sicherlich auch brauchen wirst: "forward declaration"

    Gruß,
    XSpille



  • XSpille schrieb:

    Max64 schrieb:

    Da ich "richtige" Interfaces in C++ nicht gefunden habe, habe ich diese eben als normale Klasse deklariert, was natürlich (wie ich jetzt bemerkt habe) nicht funktionieren kann. Die Klasse vererbt "kreuz und quer" und damit kommt es zu redefinitionen, was der Compiler nicht zulässt.

    google mal nach "include gurads".
    http://en.wikipedia.org/wiki/Include_guard

    Oh, diesen Teil hatte ich übersehen. Viel wichtiger finde ich hier den Hinweis auf die ODR (one-definition-rule). Das mit den Header-Dateien und Include-Guards ist natürlich eine praktische Konvention.



  • Vielen Dank erstmal für die vielen hilfreichen Antworten! Leider habe ich noch Probleme mit dem überschreiben der virtuellen Funktion (Im Laufe des try-and-error habe ich tausende unterschiedliche Fehlermeldungen erhalten; immerhin gabs da Abwechslung 😉 ).

    Ich habe nun so aufgebaut:

    Test.h:

    class Test
    {
    public:
    	Test();
    	virtual bool meth()=0;
    };
    

    Test.cpp:

    #pragma once
    #include "stdafx.h"
    #include "Test.h"
    
    Test::Test()
    {
    }
    

    Test2.h:

    #include "Test.h"
    
    class Test2 : Test
    {
    public:
    	Test2();
    
    };
    

    Test2.cpp:

    #pragma once
    #include "stdafx.h"
    #include "Test2.h"
    
    Test2::Test2()
    {
    }
    

    Ich habe verschiedene Varianten getestet, um die Methode meth in Test2 zu überschreiben (override,...), aber am Ende kam immer ein Error, der mir nicht weitergeholfen hatte. Wie würde ich die Methode überschreiben, um true zurückzugeben ;).

    C# hat mich verzogen 😞 !



  • Du musst natürlich die Methode in der konkreten Klasse implementieren.

    #include "Test.h"
    
    class Test2 : Test
    {
    public:
        Test2();
        bool meth() { return true; }
    
    };
    

  • Administrator

    Max64 schrieb:

    Vielen Dank erstmal für die vielen hilfreichen Antworten! Leider habe ich noch Probleme mit dem überschreiben der virtuellen Funktion (Im Laufe des try-and-error habe ich tausende unterschiedliche Fehlermeldungen erhalten; immerhin gabs da Abwechslung 😉 ).

    Ich geb dir einen Tipp: C# kann man vielleicht noch mit Try&Error und der MSDN erlernen. Für C++ solltest du unbedingt ein gutes Buch lesen, weil die Sprache viel umfassender ist als C#.
    http://www.c-plusplus.net/forum/251551

    Grüssli



  • Max64 schrieb:

    class Test2 : public Test // <-- 2
    {
    public:
        Test2();
        bool meth(); // <-- 1
    };
    

    1. Selbst C# verlangt das Interfaces implementiert werden.
    2. In C++ gehört hier noch ein Zugriffsmodifikator hin, im Regelfall public.



  • ...auch hat der Konstruktor in der abstrakten Basis-Klasse nicht viel zu suchen -- zumindest bei einer, die keine Datenelemente hat. Wolltest Du da einen Destruktor hinsetzen? Mach's so:

    class ABC
    {
    public:
      virtual ~ABC() {}
      virtual bool meth() const =0;
    }; //                 ^^^^^
    

    Der virtuelle Destruktor ist wichtig, wenn Du so etwas machen möchtest:

    ABC* quelle()
    {
      return new Derived(42);
    }
    
    int main()
    {
      ABC* p = quelle();
      p->meth();
      delete p; // <-- Achtung. Das geht schief, wenn der Destruktor
                // in ABC nicht virtuell ist!!!
    }
    

    Abgesehen davon, solltest Du new/delete und new[]/delete[] sowieso wenn möglich vermeiden. Du kommst mit den Standardcontainern da meistens drum herum. Dann gibt es ja noch die "Pointer-Container" von Boost und Dinge wie "auto_ptr" und "shared_ptr", die einem wenigstens das delete abnehmen.

    Ich habe mir auch erlaubt bei der "Methode" -- in C++ sagen wir dazu (nicht-statische) Elementfunktion -- einen const-Qualifizierer dazu zupacken. Ob es an dieser Stelle hier richtig ist, hängt natürlich davon ab, was meth machen soll. Du solltest Dir aber angewöhnen, allen Elementfunktionen einen solchen Qualifizierer zu verpassen, falls die jeweilige Elementfunktion den von außen beobachtbaren Zustand des Objekts nicht verändert. Damit kannst Du anderen Funktionen zB nur Lese-Zugriff geben, wenn Du willst:

    // foo will das ABC-Objekt nicht ändern, nur abfragen
    void foo(ABC const *zeiger)
    { //         ^^^^^
      // meth muss hier const-qualifiziert sein, damit
      // die folgende Zeile funktioniert:
      cout << zeiger->meth() << endl;
    }
    

    Das und vieles mehr steht in schlauen C++ Büchern. Ich empfehle Dir auch, wie Dravere, Dir ein solches zu besorgen. Du wirst es nicht bereuen; denn Du kannst viel Zeit damit sparen und Missverständnisse reduzieren. Mit Raten, Probieren und Selbstzusammenreimen bist Du in C++ sehr schlecht beraten.



  • Danke jetzt funtionierts!

    Ich habe schon ein Buch... Einführung in die Programmierung mit C++ (Bjanre Stroustrup).
    Aber ehrlich gesagt gefällt mir das Buch überhaupt nicht. Ich komme mit dem Teil nicht zurecht... Der Programmcode ist durch die Struktur einfach nicht lesbar und das Stichwortverzeichnis ist sinnlos...

    Ich brauche bestenfalls ein Buch für C++ Silverlight Embedded, da habe ich aber bisher nichts brauchbares gefunden. Optimal wären dabei noch ein paar Kapitel über XML ;-).



  • Max64 schrieb:

    Einführung in die Programmierung mit C++ (Bjanre Stroustrup)

    Sehr gutes Buch!!! 👍



  • Max64 schrieb:

    Ich habe schon ein Buch... Einführung in die Programmierung mit C++ ([Bjarne] Stroustrup). Aber ehrlich gesagt gefällt mir das Buch überhaupt nicht. Ich komme mit dem Teil nicht zurecht...

    Komisch. Ich höre sonst nur gutes über das Buch. Als Umsteiger könnte es allerdings etwas langweilig sein. Ich habe als Umsteiger TC++PL von ihm verwendet und war jedenfalls voll zufrieden. Später auch noch D&E gelesen und für gut befunden.

    Max64 schrieb:

    [...] Ich brauche bestenfalls ein Buch für C++ Silverlight Embedded, [...] Optimal wären dabei noch ein paar Kapitel über XML 😉

    Das alles in einem guten Buch zu finden, also, einen Teil zum Lernen von C++, einen Teil zu Microsoft Silverlight Embedded und einen Teil zu XML, halte ich für unwahrscheinlich. Ich finde, das passt auch nicht besonders gut in einem Buch zusammen. Silverlight sagt mir gar nichts und meine XML Kenntnisse sind rudimentär. Aber ich weiß, dass wenn man ein Buch über C++ schreibt, es schon dick genug werden kann.



  • Max64 schrieb:

    Ich brauche bestenfalls ein Buch für C++ Silverlight Embedded,

    Ist Silverlight Embedded noch relevant (Ich finde nur angaben für Windows CE 6)? Unter Windows 7 Phone sollte C# und Co mit normalen Silverlight laufen...



  • Ja Silverlight Embedded ist noch relevant für Windows Embedded Compact 7 (CTP).

    Das ist so ein Punkt, den ich an der Stelle nicht verstehe- WindowsPhone 7 läuft mit Silverlight Embedded und managed code. Die Windows Embedded Compact 7 aber nur mit native (bzw. auf jeden Fall MUSS ein native code als Schnittstelle bestehen).



  • krümelkacker schrieb:

    Viel wichtiger finde ich hier den Hinweis auf die ODR (one-definition-rule). Das mit den Header-Dateien und Include-Guards ist natürlich eine praktische Konvention.

    Welche praktisch-angewendeten Konzepte gibt es denn sonst zur Einhaltung der ODR
    um Doppeldefinitionen zu verhindern?


Log in to reply