Standard-Implementierung von "pure virtual" nicht vererbt?



  • Ich habe diese Klassen-Konstukt, welches nicht kompiliert, da die Klasse "C" die virtuelle Methode "Foo" (aus der Kalsse "A") nicht implementiert:

    class A
    {
    public:
      A() { ; }
      virtual ~A() { ; }
    
      virtual void Foo() = 0;
    };
    void A::Foo() { /* default impl */ }
    
    class B : public A
    {
    public:
      B() { ; }
      virtual ~B() { ; }
    };
    
    class C : public B
    {
    };
    
    int main(void)
    {
      C c; // Compiler error here
      return 0;
    }
    

    Das würde klappen:

    class C : public B
    {
    public:
      C() { ; }
      virtual ~C() { ; }
    
      void Foo() { A::Foo(); }
    };
    

    ...ist aber nicht schön, weil die Klasse "C" über "A" wissen müsste, was sicherlich nicht das ist, was an mit der Vererbung erreichen möchte.

    Ich frage mich nun, warum das so ist. Ich dachte immer, dass Standard-Implementierungen auch weiter vererbt werden, das scheint aber nicht der Fall. Welche Möglichkeiten habe ich, das zu umgehen?

    Compiler: MinGW GCC 4.7.1.



  • MortenMacFly schrieb:

    Welche Möglichkeiten habe ich, das zu umgehen?

    😕 Die Methode nicht pure virtual machen vielleicht? (Und bitte lass diese ; aus lehren Methoden, aua.)
    Und dass C von A erbt (was es indirekt tut), ohne A zu kennen ist mal ziemlicher Quatsch.



  • virtual void Foo() = 0;
    };
    void A::Foo() { /* default impl */ }
    

    Öh, muss ich verstehen, was das soll? Entweder man macht es pure virtual, oder eben implementiert es. Warum beides? Macht doch gar keinen Sinn.



  • Ok - Danke! Werd' mal sehen, was ich daraus mache...



  • Dadurch, dass du die Methode pure virtual machst, sagst du doch gerade, dass diese Methode in einer abgeleiteten Klasse implementiert werden muss!? Oder was dachtest du, mit der pure virtual Methode zu erreichen?



  • Es gibt bestimmt Umsteiger aus anderen Sprachen, die gelesen haben dass Interfaces in C++ durch Klassen mit pure virtual Methoden umgesetzt werden. Vielleicht weht daher der Wind, aber keine Garantie 😉



  • Also ich kenne keine andere Sprache, in der man Methoden eines Interface nicht implementieren muss. Was für einen Sinn hätte das?



  • Na ich meinte es jetzt schon so, dass in dem gegebenen Beispiel A ein interface sein soll. Darum alles pure virtual, weil eben Interface. Und dann soll die eine Methode halt auch noch eine Standard-Implementierung haben. Ist doch klar!



  • Ok, ich kenn aber keine Sprache, in der Methoden eines Interface eine Standardimplementierung haben können. Was für einen Sinn sollte das auch haben, Interfaces können doch normalerweise keine Datenmember haben!?
    C++ ist da wohl eher die Ausnahme, da man abstrakte Methoden in C++ tatsächlich implementieren kann. Das ändert dann aber nichts daran, dass die Klasse abstrakt ist und nicht instanziert werden kann...



  • Ich habe ja mit keinem Wort gesagt, dass der Gedankengang richtig wäre. Ich habe nur nachzuvollziehen versucht, weshalb jemand auf dem pure virtual beharren möchte, trotz der Tatsache, dass er eine Standardimplementierung geben möchte!



  • Schon klar, ich hab nur versucht, den Fehler in diesen Gedankengängen aufzuzeigen, für den Fall, dass einer davon tatsächlich der Grund für die Verwirrung wäre. 😉

    Abgesehen davon: Man kann in C++ sogar eine "Standardimplementierung" einer pure virtual Methode liefern. Nur muss man diese aus der abgeleiteten Klasse explizit aufrufen:

    class Base
    {
    public:
      virtual void blub() = 0
      {
        // ...
      }
    };
    
    class Derived : public Base
    {
    public:
      virtual void blub()
      {
        Base::blub();
      }
    };
    

    Denn so lange die rein virtuelle Methode nicht überschrieben wird, ist die Klasse abstrakt...



  • @dot

    class Base 
     { 
    public: 
       virtual void blub() = 0 
       { 
         // ... 
       } 
     };
    

    Die Schreibweise ist zwar naheliegend, aber ein Syntax-Error (warum auch immer).
    Bei virtual pure musst du die Methode ausserhalb der Klassendefinition definieren.



  • Da hast du natürlich absolut recht; da sieht man schön, wie oft man sowas braucht, dass ich nichtmal die korrekte Syntax zusammenbring...



  • Das wird aber in Effective C++ sogar explizit als Methode empfohlen, eine Standardimplementierung zur Verfügung zu stellen. Mit dem Vorzug, dass der Erbende noch mal drüber nachdenken muss, ob die Standardimplementierung wirklich dass ist, was gewollt wird.



  • Nun, ich verwend vollwertige Vererbung generell eher selten. Einzig mein GUI System mach heftigen Gebrauch von allen möglichen Formen von Vererbung (sogar virtual protected 😃 ) und dort will man nicht sämtliche Methoden immer und überall neu implementieren...



  • und was war die Situation, in der du virtual protected brauchtest?



  • Nunja, nehmen wir als Beispiel das Panel Control:

    class Panel : public virtual Control, protected virtual Parent
      {
        // ...
      };
    

    Panel implementiert das öffentliche Interface Control (Virtual Inheritance ist für Interfaces notwendig, damit z.B. die Methoden eines Interface teilweise in einer anderen Basisklasse implementiert sein können). Intern implementiert Panel auch das Interface Parent. Würde Panel allerdings virtual private von Parent ableiten, könnte nun niemand mehr von Panel ableiten, da bei Virtual Inheritance der Konstruktor der virtuellen Basisklasse in der most derived Class verfügbar sein muss. Daher virtual protected...

    Edit: Die Alternative wäre vermutlich, beim Ableiten von Panel immer auch nochmal private virtual von Parent abzuleiten...


  • Mod

    dot schrieb:

    Edit: Die Alternative wäre vermutlich, beim Ableiten von Panel immer auch nochmal private virtual von Parent abzuleiten...

    oder den Typ der Basisklasse auf andere Weise zu finden

    class Parent {};
    class Control {};
    class Panel : public virtual Control, private virtual Parent
    {};
    
    class MyPanel : public Panel
    {
       MyPanel() : Parent(), Panel()  {} // error
       MyPanel() : ::Parent(), Panel()  {} // ok
    };
    


  • Das geht? Interessant...Nachteil ist aber natürlich, dass der Code der abgeleiteten Klasse dadurch von der Basisklassenliste der Basisklasse abhängig wird, d.h. wenn irgendwo im Inheritance Graph eine neue virtual Base dazukommt, muss ich den Code aller Blätter anpassen!?


  • Mod

    dot schrieb:

    Das geht? Interessant...Nachteil ist aber natürlich, dass der Code der abgeleiteten Klasse dadurch von der Basisklassenliste der Basisklasse abhängig wird...

    Das ist er doch sowieso schon?

    Schöner wäre wahrscheinlich

    class MyPanel : public Panel
     {
       typedef ::Parent Parent;
       MyPanel() : Parent(), Panel()  {}
     };
    


  • Nicht unbedingt:

    class Parent {}; 
    class Control {}; 
    class Panel : public virtual Control, protected virtual Parent 
    {}; 
    
    class MyPanel : public Panel 
    { 
       MyPanel() {} // ok
    };
    

Anmelden zum Antworten