mehrfachvererbung design problem



  • hola

    ich hab 2 baseklassen, die auch direkt verwenden werden. das i is ein handle das beide benoetigen, aber: in myclass haben sie immer den selben wert. ich brauch also bei der vererbung eigendlich nur ein i.

    class base1
    {
       int i;
    };
    
    class base2
    {
       int i;
    };
    

    nun erbe ich von beiden klassen:

    class myclass : public base1, public base2
    {
    
    };
    

    nun haette ich einen mehrdeutigen zugriff auf i. wie soll ich base1 und base2 implementieren um das problem zu loesen ? optimal waere wenn ich auch nur noch ein i in myclass haette.

    Meep Meep


  • Mod

    Das ist der klassische diamond of death:
    Google: diamond of death

    Technische Lösung: Virtuelle Vererbung.

    Richtige Lösung: Nochmal darüber nachdenken, ob das Konstrukt hier wirklich richtig ist. Vererbung wird meistens übelst missbraucht, meistens um Code zu sparen oder weil man denkt, ein Wurstbrot wäre ein Supermarkt. Wenn du nicht gerade in Java bist, dann sind lange Vererbungshierarchien ein Zeichen für einen Designfehler. Und Mehrfachvererbung noch viel mehr. Viele Sprachen bieten diese Möglichkeit nicht einmal an, da man es praktisch nie braucht und es nur Probleme macht. (Das soll natürlich nicht heißen, dass du hier nicht den einen Fall von einer Million vorliegen haben könntest, wo der diese Hierarchie mal tatsächlich sinnvoll ist. Aber bist du dir wirklich sicher?)



  • ich ueberarbeite zur zeit meine bibliothek fuer ein binaeres bussystem.
    nun will ich aehnlich der stl streams die io-klasse in input und output aufspalten. hintergrund: frueher konnte man das bussystem nicht einseitig benutzen sondern nur bidirektional, nun geht es aber.
    den int in meinem beispielcode hab ich nur zur anschauung verwendet. es sind aber um einiges mehr daten die benoetigt werden, aber trotzdem immer die selben sind. bei nur einem int waere es mir egal gewesen ob der doppelt vorhanden ist oder nicht.

    Meep Meep



    1. Müssen die beiden Basisklassen Baisklassen sein? Man sollte Komposition in den meisten Fällen vorziehen.

    2. Wenn das i beidesmal das selbe Handle ist, ist es dupliziertes Verhalten, wenn du es in beiden Base*-Klassen implementiert hast. Puristen würden das wohl als Anlass nehmen, das Handle in eine Basisklasse auszulagern mit allem was dazu gehört (gibt bestimmt mehr als nur den int, vielleicht irgendwelche Hilfsmethoden für das Handle?).
      mit "HandleBase", den beiden Base*-Klassen und myclass hättest du dann den typischen Diamond of Death - das übliche Literaturbeispiel für virtuelle Vererbung.

    3. Eine Möglichkeit wäre, für die Handle-Geschichte eine eigene Klasse zu schreiben und den Base1/Base2-Konstruktoren einen Pointer oder eine Referenz auf eine solche Handleklasse zu übergeben. Konkrete Klassen wie myclass sind dann dafür zuständig, ein Handle-Objekt zu erzeugen und die Referenz an die Base-Konstruktoren zu übergeben.

    struct Handle {
      Handle() : i(/*get Handle value*/) {}
      int i;
      /* Methoden? */
    };
    
    struct Base1 {
      Base1(Handle* hndl);
    };
    
    struct Base2 {
      Base2(Handle* hndl);
    };
    
    struct myclass : Base1, Base2 {
      Handle h;
      myclass() : Base1(&h), Base2(&h), h() {}
    };
    

  • Mod

    Ich würde die IOStreams nicht als Designvorbild nehmen. Die stammen aus grauer C++-Vorzeit, lange bevor das was man heutzutage "gutes C++" nennt erfunden wurde. Heutzutage würde man sie sicherlich ganz anders aufziehen, die Mehrfachvererbung wäre dabei eine der Stellen, die man ändern würde.

    Es gibt Alternativen, habe gerade leider keine Zeit diese zu erklären. Vielleicht in ein paar Stunden. So wie ich das Forum kenne, wirst du bis dahin aber bereits mit Antworten überschwemmt werden. edit: Hah, pumuckl war sogar schneller noch als diese Antwort 🙂



  • Meep Meep schrieb:

    nun will ich aehnlich der stl streams die io-klasse in input und output aufspalten.

    Die iostreams lösen das so, dass sie intern eine Referenz auf einen streambuf haben, der als Basisklasse für die eigentlichen io-Geschichten fungiert. istreams benutzen nur die Lese-Operationen des jeweiligen Streambufs, ostreams nur die Schreiboperationen. Ganz grob betrachtet ist das ein ähnliches System wie das von mir vorgeschlagene, die Handle-Klasse entspräche dem streambuf. Der istream-Ctor sieht z.B. so aus:

    explicit  istream (streambuf * sb);
    


  • SeppJ schrieb:

    Es gibt Alternativen, habe gerade leider keine Zeit diese zu erklären.

    ich waere an diesen rein informationstechnisch interessiert. falls du also mal die zeit haettest diese aufzufuehren waere ich dir dankbar.

    @pumuckl
    wie die STL das macht ist mir schon klar. meine derzeitige klasse entspricht eigendlich der streambuf. ich werde mal noch etwas darueber nachdenken ob ich nicht einen ganz anderen ansatz finde. irgendwie bin ich damit nicht so zu frieden.

    Meep Meep



  • Meep Meep schrieb:

    meine derzeitige klasse entspricht eigendlich der streambuf. ich werde mal noch etwas darueber nachdenken ob ich nicht einen ganz anderen ansatz finde. irgendwie bin ich damit nicht so zu frieden.

    Wenn du das Pendant zum Streambuf benutzt, warum brauchst du dann die beiden Basisklassen? Biete doch im Streambuf sowohl input als auch output an, erst die kapselnde Klasse (~istream/ostream/iostream) ist dafür zuständig, dass der streambuf nur "richtig" angesprochen wird.
    Konkretes Beispiel ist der std::filebuf, der für fstream, fostream, fistream gleichermaßen zuständig ist.



  • Falls du wirklich die Mehrfachvererbung brauchst: ist das i bei beiden Klassen identisch? Dann auslagern in eine virtuelle Basisklasse. Falls nicht musst du mit Base1::i referenzieren oder

    using Base1::i;
    

    benutzen.


Anmelden zum Antworten