Braucht man Mehrfachvererbung



  • Shade Of Mine schrieb:

    Lustigerweise sind Vererbungshierachien in C++ deshalb meistens viel schöner und flacher und vorallem weniger breit als in Java

    das liegt aber eher daran, dass c++ user davor zurückschrecken und gleich 'designfehler' und laufzeitprobleme wittern, wenn mal geerbt wird oder gar 'virtual' zum einsatz kommt.
    🙂



  • java-freak schrieb:

    Shade Of Mine schrieb:

    Lustigerweise sind Vererbungshierachien in C++ deshalb meistens viel schöner und flacher und vorallem weniger breit als in Java

    das liegt aber eher daran, dass c++ user davor zurückschrecken und gleich 'designfehler' und laufzeitprobleme wittern, wenn mal geerbt wird oder gar 'virtual' zum einsatz kommt.
    🙂

    Keine Frage - das ist eben das Lustige das passiert wenn Paradigmen übernommen werden ohne sie zu verstehen.

    In Java hat man viel Vererbung, Laufzeitpolymorphie wohin man nur schaut - in C++ dagegen fürchtet man sich fast vor einem virtual.

    Es gibt viele solche lustige Erscheinungen - das bedeutet aber nicht dass die Sprache deshalb schlecht sei 😉



  • Also ich benutze dann virtual, wenn ich es brauche. Nicht mehr und nicht weniger.



  • Artchi schrieb:

    Also ich benutze dann virtual, wenn ich es brauche. Nicht mehr und nicht weniger.

    Gute Programmierer programmieren gut 😉

    Es ist lediglich interessant sich durchschnittlichen oder eher schwächeren Code anzusehen. Da strotzt Java vor Vererbung und 100.000 Listener die überall hingeerbt werden und in C++ hat man dagegen nur ein paar functors die durch die Gegend fliegen.



  • Shade Of Mine schrieb:

    Es ist lediglich interessant sich durchschnittlichen oder eher schwächeren Code anzusehen. Da strotzt Java vor Vererbung und 100.000 Listener die überall hingeerbt werden und in C++ hat man dagegen nur ein paar functors die durch die Gegend fliegen.

    Ja, nur in Java hast du nicht sowas wie Functors. Nennt sich in anderen Sprachen glaube ich Delegate? In Java bleibt dir somit nichts anderes übrig als das Listener-Interface zu "vererben".

    Und natürlich finde ich std::tr1::function toll, dann brauche ich keine Vererbung, um eine beliebige (Member-) Function zu verbinden (std::tr1::bind). Ein virtual kann man so natürlich einsparen.

    struct Observer
    {
        void update();
    };
    
    Observer obs;
    std::tr1::function<void (void)> obs_fun = std::tr1::bind(&Observer::update, &obs);
    
    // später:
    obs_fun();  // ruft  obs.update() auf
    

    obs_fun kann natürlich jede andere beliebige (Member-)Function binden.

    EDIT: function und bind kommen auch in C++0x rein, und somit direkt im Namespace std.



  • Shade Of Mine schrieb:

    Es ist lediglich interessant sich durchschnittlichen oder eher schwächeren Code anzusehen. Da strotzt Java vor Vererbung und 100.000 Listener die überall hingeerbt werden und in C++ hat man dagegen nur ein paar functors die durch die Gegend fliegen.

    Sehr objektiver Sprachenvergleich. 👍 🤡



  • Artchi schrieb:

    Ja, nur in Java hast du nicht sowas wie Functors. Nennt sich in anderen Sprachen glaube ich Delegate? In Java bleibt dir somit nichts anderes übrig als das Listener-Interface zu "vererben".

    Schlechter Java Code:

    public class Frame1 extends JFrame implements AListener, BListener, CListener, DListener... {
    ...
      foo.registerFooListener(this);
      bar.registerBarListener(this);
    }
    

    Guter Java Code:

    public class Frame1 extends JFrame {
    ...
      foo.registerFooListener(new FooListener() {
        ...
      });
      bar.registerBarListener(new BarListener() {
        ...
      })
    
    }
    

    aber scheinbar sind wir noch nicht reif genug um über praktiken zu sprechen die sich eingebürgert haben...



  • Du hast Anonyme Klassen... schön, habe ich hier in meinen Projekten auch. Trotzdem ist es Vererbung! Nur das am Ende die spezielle Implementierung keinen Namen hat. Du hast das Interface-Vererben-Problem nicht umgangen, nur verlagert. Von den Interface bin ich immer noch abhängig und Polymorphy ist es trotzdem noch!



  • Artchi schrieb:

    Du hast Anonyme Klassen... schön, habe ich hier in meinen Projekten auch. Trotzdem ist es Vererbung! Nur das am Ende die spezielle Implementierung keinen Namen hat. Du hast das Interface-Vererben-Problem nicht umgangen, nur verlagert. Von den Interface bin ich immer noch abhängig und Polymorphy ist es trotzdem noch!

    Habe ich je gesagt dass das das Problem ist?

    Das Problem sind tiefe und breite Vererbungshierachien.



  • Das grundsätzliche Problem bei der Implementation der Mehrfachvererbung ist doch die Relativität der Objektadresse. Als unmittelbare Konsequenz sind C++-Methodenzeiger für Delegate-Zwecke nahezu unbrauchbar, wie Don Clugston anschaulich darlegt. Auch laufen dadurch sich ergebende Unausweichlichkeiten im Umgang mit Objektzeigern dem intuitiven Verständnis des Programmcodes zuwider. Wie sollte man auf die Idee kommen, daß eine harmlose Anweisung wie

    Base* b;
    Derived* d;
    ...
    b = d;
    

    die Zeigeradresse verändert? Und gesetzt, Derive leite von mehreren Klassen ab, die ihrerseits von Base abstammen, was bei polymorphielastiger Programmierung vorkommen kann: mit welch überflüssiger Verzweiflung reagiert ein Novize auf die dröge Verweigerung des Compilers, dasselbe Stück Codes zu übersetzen?

    Mit Interfaces im herkömmlichen Sinne - so wie sie auch in Delphi und vermutlich in Java existieren - wird dieses Problem vermieden, da sie keine Datenelemente enthalten. Das hat sich in vielen Situationen, ob nun Java, .NET oder COM, als sinnvoll erwiesen (wenngleich in Java ein gewisses Überreizungspotential bestehen mag). Nach einer praktischen und alternativlosen Anwendung der Mehrfachvererbung von C++ jedoch suche ich noch immer vergeblich.



  • Technisch zwar sehr schoen, aber was interessiert mich das als Programmierer?

    Wenn man eine schoene Library wie zB boost oder dergleichen hat, wird die ganze Implementation vor einem versteckt. Deshalb _kann_ ich Member function pointer problemlos benutzen, oder aber functors oder aber den Java Ansatz mit laufzeit Polymorphie.

    Ich habe in C++ also alle 3 Ansaetze die problemlos funktionieren:

    Delegates (methodenzeiger)
    Interfaces (laufzeitpolymorphie)
    functors (compiletime polymorpgie)

    Wo genau ist das jetzt schlecht? Dass bei Methodenzeigern intern furchtbare Sachen geschehen stimmt ja - aber andererseits: was bei einer referenzierung eines Objektes in Java intern passiert, ist auch nicht schoen. Dauernd wird das Objekt umherkopiert - der Zeiger aendert sich also auch 😉

    Interessiert aber Niemanden, da es perfekt versteckt ist.



  • Ohne Mehrfachvererbung sind einige Probleme nicht OO-mäßig lösbar.

    Objektorientierung wird mit nichten durch Vererbung geschweige denn Mehrfachvererbung definiert. Sie setzt sie nicht einmal zwingend voraus.
    Häufig sind Alternativen wie Komposition/Delegation oder sonstige Entwurfsmuster besser (um nicht zu sagen "objektorientierter") als Vererbung.



  • Shade Of Mine schrieb:

    Technisch zwar sehr schoen, aber was interessiert mich das als Programmierer?

    Wie kann ein C++-Programmierer sich ernsthaft eine solche Frage stellen? Noch nie im Assembler-Debugger gelandet?

    Shade Of Mine schrieb:

    Wenn man eine schoene Library wie zB boost oder dergleichen hat, wird die ganze Implementation vor einem versteckt. Deshalb _kann_ ich Member function pointer problemlos benutzen, oder aber functors oder aber den Java Ansatz mit laufzeit Polymorphie.

    Ich habe in C++ also alle 3 Ansaetze die problemlos funktionieren:

    Delegates (methodenzeiger)
    Interfaces (laufzeitpolymorphie)
    functors (compiletime polymorpgie)

    Bekanntermaßen ermöglicht C++ insbesondere mittels TMP die Möglichkeit, so ziemlich jedes Paradigma umzusetzen, sei es noch so schmerzhaft. Die Sprache bietet andererseits, und damit bleibt sie in der Nähe des Ideals von C, sehr wenig Compilermagie. Infolgedessen müssen alle Sprachdefizite durch Bibliotheken ausgeglichen werden. Das erhöht die Kompilierzeiten, die Fehleranfälligkeit, die Uneinheitlichkeit und die Komplexität.

    Um aber dem unbedingten Ideal der Plattformunabhängigkeit zu genügen, offeriert die Standardbibliothek zumeist eher minimalistische Hilfsmittel. Selbst so grundlegende Dinge wie funktionierende Funktionszeiger muß man über Boost nachrüsten (oder selbst schreiben, falls die Boost-Lösung nicht ausreichend performant oder in anderer Hinsicht unzulänglich ist; Stichwort Uneinheitlichkeit). Es wäre so unsagbar einfach gewesen, analog zu Borlands C++Builder-Compilererweiterung __closure ein Schlüsselwort zu spezifizieren, das eine einheitliche Syntax für an ein Objekt gebundene Methodenzeiger einführt. Sie sind wesentlich leichter zu implementieren als Methodenzeiger, außerdem maximal performant. Aber dafür wäre ja ein neues Schlüsselwort notwendig gewesen. Da nimmt man dann lieber den Boost-Workaround in den nächsten Standard auf.

    Das ist natürlich kein Einzelfall. Glücklicherweise gibt es durchaus positive Ansätze im neuen Standardentwurf, z.B. die Neuinterpretation des auto-Schlüsselwortes. Von den produktiven Möglichkeiten einer wirklich innovativen Sprache wie z.B. Chrome ist C++ aber dennoch weit entfernt. Und die Komplexität, die C++0x erreichen wird, ist für einen Neueinsteiger unüberschaubar bis abschreckend.

    Nun bin ich aber ein wenig vom Thema abgekommen 😉
    Kurzum, hast du Mehrfachvererbung schon einmal benötigt? Nein? Dann stimmst du mir sicherlich darin zu, daß es ohne erkennbaren Mehrwert zusätzliche Komplexität verursacht und ein eigentlich ganz praktisches Sprachfeature wie Methodenzeiger verkrüppelt.

    Shade of Mine schrieb:

    Wo genau ist das jetzt schlecht? Dass bei Methodenzeigern intern furchtbare Sachen geschehen stimmt ja - aber andererseits: was bei einer referenzierung eines Objektes in Java intern passiert, ist auch nicht schoen. Dauernd wird das Objekt umherkopiert - der Zeiger aendert sich also auch 😉

    In einer Bytecode-Sprache ist das dann doch noch etwas anderes als in einem besseren C-Derivat 😉



  • audacia schrieb:

    Wie sollte man auf die Idee kommen, daß eine harmlose Anweisung wie

    Base* b;
    Derived* d;
    ...
    b = d;
    

    die Zeigeradresse verändert?

    Gegenfrage: warum sollte man sich überhaupt irgendwelche Gedanken über Adressen machen müssen?



  • audacia schrieb:

    Shade Of Mine schrieb:

    Technisch zwar sehr schoen, aber was interessiert mich das als Programmierer?

    Wie kann ein C++-Programmierer sich ernsthaft eine solche Frage stellen? Noch nie im Assembler-Debugger gelandet?

    In produktivsystemen? Nein. Zum rumspielen: oefters.

    Es wäre so unsagbar einfach gewesen, analog zu Borlands C++Builder-Compilererweiterung __closure ein Schlüsselwort zu spezifizieren, das eine einheitliche Syntax für an ein Objekt gebundene Methodenzeiger einführt.

    Komplett realitaetsfern.

    Sorry aber _alles_ ist so leicht implementierbar. Threads? klar, machen wir ein __thread keyword. sockets? gogo __socket. Wie waers mit multicore suppoert: __multicore {}.

    Nur leider geht es komplett an der realitaet vorbei. Zu dem zeitpunkt als der C++ standard entschieden wurde (mitte der '90er) waren closures und delegates kein essentielles notwendiges feature - du kannst es mit einer library ja problemlos implementieren.

    der neue c++ standard hat closures. wie das implementiert wird, ist komplett egal.

    Von den produktiven Möglichkeiten einer wirklich innovativen Sprache wie z.B. Chrome ist C++ aber dennoch weit entfernt.

    Und jetzt denk mal nach warum C++, Java, C# und Konsorten soviel erfolg haben. Liegt es daran dass die Sprachen so toll designed sind? Nein - es liegt daran dass diese Sprachen das know how verwenden das die leute haben.

    Kurzum, hast du Mehrfachvererbung schon einmal benötigt? Nein? Dann stimmst du mir sicherlich darin zu, daß es ohne erkennbaren Mehrwert zusätzliche Komplexität verursacht und ein eigentlich ganz praktisches Sprachfeature wie Methodenzeiger verkrüppelt.

    Dauernd. Ich sehe sie auch dauernd. Ich habe noch kein Java Programm gesehen dass nicht mehrfachvererbung einsetzt.

    Lies mal meinen Beitrag mit ueber konkreten Klassen und Konzepte.

    Mehrfachvererbung wird sehr oft eingesetzt und ist meistens etwas gutes. Die Leute erschrecken nur davor, weil sie immer an die schlechten beispiele denken.

    Aber was das wirkliche Problem ist, ist das erben von konkreten Klassen.

    Shade of Mine schrieb:

    Wo genau ist das jetzt schlecht? Dass bei Methodenzeigern intern furchtbare Sachen geschehen stimmt ja - aber andererseits: was bei einer referenzierung eines Objektes in Java intern passiert, ist auch nicht schoen. Dauernd wird das Objekt umherkopiert - der Zeiger aendert sich also auch 😉

    In einer Bytecode-Sprache ist das dann doch noch etwas anderes als in einem besseren C-Derivat 😉

    Warum?

    PS:
    ich sehe in Chrome nichts besonderes wo ich sagen wuerde "wow". Ein paar features sehen nett aus, aber das alleine macht die sachen nicht wirklich sonderlich interessant. Vorallem da es eine fuelle an schluesselwoertern gibt - sowas ist idR ein schlechtes zeichen.



  • tfa schrieb:

    Objektorientierung wird mit nichten durch Vererbung geschweige denn Mehrfachvererbung definiert. Sie setzt sie nicht einmal zwingend voraus.
    Häufig sind Alternativen wie Komposition/Delegation oder sonstige Entwurfsmuster besser (um nicht zu sagen "objektorientierter") als Vererbung.

    Sofern wir uns hier im Kontext statisch getypter OO-Sprachen wie C++ oder Java bewegen, würde ich gerne wissen, was dir die Komposition oder Delegation bringt, wenn die Objekte, an die du delegierst nicht polymorph sein dürfen.



  • Bashar schrieb:

    Sofern wir uns hier im Kontext statisch getypter OO-Sprachen wie C++ oder Java bewegen, würde ich gerne wissen, was dir die Komposition oder Delegation bringt, wenn die Objekte, an die du delegierst nicht polymorph sein dürfen.

    In Java kannst Du dank Interfaces problemlos Vererbung durch Komposition + Delegation ersetzen und trotzdem noch Polymorphie nutzen. Du definierst einfach ein Interface + eine Implementierung dieses Interfaces. Diese Implementierung kannst Du dann per Komposition in Deinen Objekten benutzen (statt davon zu erben). Zusätzlich implementieren diese Objekte dann noch das Interface und delegieren auf das Kompositum.



  • byto schrieb:

    In Java kannst Du dank Interfaces problemlos Vererbung durch Komposition + Delegation ersetzen und trotzdem noch Polymorphie nutzen.

    Ein Interface zu implementieren ist für meine Begriffe auch Vererbung (der Schnittstelle). Falls man zwischen Vererbung und Subtyping einen Unterschied macht, stimmt das natürlich.



  • Also ich hab mir den Thread nicht durchgelesen, aber geb mal meinen Senf dazu ab.
    Bei komplett eigenen Klassen kann mans vermeiden, denke ich, aber ansonsten ises ganz praktisch, wenn es zum Beispiel ein Fenster ist und von einer GUI Klasse erbt und du noch eine andere Vererbung rein bringen willst, oder so etwas ähnliches.
    Kann aber auch sonst praktisch sein, für SFML brauchst du ein SFML Fenster zum rein zeichnen, wie integriert man das in wxWidgets? Eine Klasse die von wxWindow und sf::Window erbt, dann brauchte man eigentlich nur noch ein paar Zeilen Code, nich viel.



  • @Bashar: Jo, Java-Interface-Implementierung ist im Grunde ja nichts anderes als Vererbung. Aber man liest halt immer wieder als Kritik am Interface-Konzept von Java, dass man damit Code duplizieren muss. Und das ist halt schlicht falsch, wenn mans auf oben beschriebenem Weg macht.


Anmelden zum Antworten