Braucht man Mehrfachvererbung
-
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.
-
byto schrieb:
@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.
Wie genau komm ich an IFooable und AbstractFoo vorbei?
-
camper schrieb:
Gegenfrage: warum sollte man sich überhaupt irgendwelche Gedanken über Adressen machen müssen?
Schreibt der Standard vor, daß static_cast <Base*> (reinterpret_cast <Derived*> (0)) == 0 ist, auch wenn Base nicht am Beginn des Objektlayouts von Derived liegt? Falls nicht, müßte man p ? static_cast <Base*> (p) : 0 schreiben.
Ein anderer, wesentlich unangenehmerer Aspekt im Zusammenhang mit Methodenzeigern: seltsamerweise darf (oder wenigstens: sollte) man einem Methodenzeiger für eine Basisklasse keine Methode einer abgeleiteten Klasse zuweisen. Tut man es doch (oder erzwingt es mittels Cast), so passieren haarsträubende Dinge:
class Base { protected: int i; public: Base (void) : i (42) {} int func(void) { return i; }; }; typedef int (Base::* basemember_t) (void); class Base2 { int j; public: Base2 (void) : j (1337) {} }; class Derived : public Base2, public Base { public: int new_func(void) { return i; }; }; void foo (void) { Derived d; int i; basemember_t m = static_cast <basemember_t> (&Derived::new_func); i = (d.*m) (); }
Dies scheint legales C++ zu sein, jedenfalls kompilieren Comeau, MSVC und BCC das anstandslos (MSVC ist wenigstens so entgegenkommend und warnt vor möglichen Konsequenzen). Ob aber der Wert von i nun erwartungsgemäß 42 oder aber undefiniert ist, hängt schlicht davon ab, in welcher Reihefolge die Basisklassen von Derived genannt werden.
Falls du meinst, so etwas tue man doch nicht: Erstens wird fast alles, was der Compiler erlaubt, auch getan, und zweitens tut Microsoft das höchstselbst in den MFC.
Shade Of Mine schrieb:
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.
Du machst es dir aber sehr einfach damit, meine Argumentation ad absurdum zu führen
Um vorab einem Mißverständnis vorzubeugen: obgleich es so heißt, hat __closure nichts mit dem zu tun, was heutzutage als Closure bezeichnet wird; eher mit Delegates. Closures sind recht schwierig zu implementieren, außerdem auch eine eher neue Erscheinung.
Zum Zeitpunkt der Verabschiedung des C++-Standards waren Delegates immerhin so gefragt, daß Borland sich schon für die OWL mit einer Compilererweiterung behelfen mußte und Qt das präprozessorgestützte Signal/Slot-System einführte, um genau diesem Mangel beizukommen. Und vor 1998 waren die meisten Compiler ohnehin nicht sattelfest genug, um all die templatebasierten Workarounds, die in der Theorie möglich und heute in Boost implementiert sind, verstehen zu können.
Solltest du weiterhin daran zweifeln, so empfehle ich dir dringend, oben bereits verlinkten Artikel von Don Clugston zu lesen.
Shade Of Mine schrieb:
der neue c++ standard hat closures. wie das implementiert wird, ist komplett egal.
Nein.
Shade Of Mine schrieb:
Dauernd. Ich sehe sie auch dauernd. Ich habe noch kein Java Programm gesehen dass nicht mehrfachvererbung einsetzt.
Ich fragte dich nach Mehrfachvererbung, nicht nach der Implementation mehrerer Interfaces. Hättest du meinen vorletzten Post genau gelesen, hättest du feststellen können, daß ich ausdrücklich dazwischen unterscheide:
*this schrieb:
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.
Shade Of Mine schrieb:
Aber was das wirkliche Problem ist, ist das erben von konkreten Klassen.
Welches Mehrfachvererbung heißt
Daß eine Vielzahl an Schlüsselwörtern eine schlechte Sprache ausmacht, ist ein Vorurteil von C- und C++-Programmierern.
-
Shade Of Mine schrieb:
byto schrieb:
@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.
Wie genau komm ich an IFooable und AbstractFoo vorbei?
Du kommst damit um die Vererbung im herkömmlichen Sinne vorbei, also mittels extends. Genau das kann nämlich böse werden und zu unwartbarem Code führen, wenn man irgendwann unüberschaubar viele Vererbungshierarchien hat.
Natürlich hat man weiterhin Interfaces und implementiert diese, was prinzipiell auch ne Form von Vererbung ist. Aber es wird hier ja hoffentlich niemand ernsthaft daran zweifeln, dass es sinnvoll ist, hin zu Schnittstellen zu programmieren.
-
audacia schrieb:
Dies scheint legales C++ zu sein, jedenfalls kompilieren Comeau, MSVC und BCC das anstandslos (MSVC ist wenigstens so entgegenkommend und warnt vor möglichen Konsequenzen). Ob aber der Wert von i nun erwartungsgemäß 42 oder aber undefiniert ist, hängt schlicht davon ab, in welcher Reihefolge die Basisklassen von Derived genannt werden.
char* p=0;
*p=0;ist auch legal weil es kompiliert - aber so ganz definiert ist das verhalten dann doch nicht.
Falls du meinst, so etwas tue man doch nicht: Erstens wird fast alles, was der Compiler erlaubt, auch getan, und zweitens tut Microsoft das höchstselbst in den MFC.
Man tut es nicht und wenn man es tut, dann weil man die konsequenzen kennt.
C++ verbietet es einem nicht in den fuss zu schiessen.
Du machst es dir aber sehr einfach damit, meine Argumentation ad absurdum zu führen
Weil die Argumentation absurd _ist_. Mehr feature will man immer haben aber _irgendwo_ muss eine grenze gezogen werden. man kann nicht _alles_ haben. ist einfach ein fakt.
Zum Zeitpunkt der Verabschiedung des C++-Standards waren Delegates immerhin so gefragt, daß Borland sich schon für die OWL mit einer Compilererweiterung behelfen mußte und Qt das präprozessorgestützte Signal/Slot-System einführte, um genau diesem Mangel beizukommen. Und vor 1998 waren die meisten Compiler ohnehin nicht sattelfest genug, um all die templatebasierten Workarounds, die in der Theorie möglich und heute in Boost implementiert sind, verstehen zu können.
Selbiges fuer Threads und 100.000 andere Sachen. Man kann nicht alles haben, ist einfach so.
Solltest du weiterhin daran zweifeln, so empfehle ich dir dringend, oben bereits verlinkten Artikel von Don Clugston zu lesen.
kenn ich schon
Shade Of Mine schrieb:
der neue c++ standard hat closures. wie das implementiert wird, ist komplett egal.
Nein.
Was genau koennen die C++ lambda ausdruecke nicht was du von einem closure erwartest?
Ich fragte dich nach Mehrfachvererbung, nicht nach der Implementation mehrerer Interfaces. Hättest du meinen vorletzten Post genau gelesen, hättest du feststellen können, daß ich ausdrücklich dazwischen unterscheide:
Und ich sehe den Unterschied einfach nicht. Ein Interface ist ein anderer Name fuer ein Konzept.
Manchmal hilft es nicht dem Marketing zu vertrauen sondern sich tiefer mit den Konzepten zu befassen. Es ist einfach keine genuegende Definition zu behaupten dass ein Interface keine logik enthalten duerfen.
Das mag fuer einzelne Sprachen ja stimmen, aber eben nur fuer die Implementierung einzelner Sprachen. Nicht im grossen Kontext. Ein Interface ist eine Schnittstelle - ein Konzept. Warum sollte ich nicht ein "Interface" nehmen koennen und logik einbauen. Dadurch aendert sich das Konzept ja kein bisschen.
Der Grund warum manche Sprachen Interfaces anbieten hat technische Gruende. Man tut sich naemlich schwer sonst von einer root Klasse ableiten zu koennen ohne dauernd deadly diamonds of death zu bauen...
Shade Of Mine schrieb:
Aber was das wirkliche Problem ist, ist das erben von konkreten Klassen.
Welches Mehrfachvererbung heißt
Und das ist eben nur zum Teil richtig.
Mehrfachvererbung in c++:
class Test : private boost::nocopyable, public std::vector, hashable { };
Es ist mehrfachvererbung aber ich erbe nur von einer konkreten Klasse.
hashable ist kein interface im Java sinn - es enthaelt zB die fertig implementierte non virtual methode "hash" die anhand der adresse des this Zeigers einen hashwert liefert.
Aber in meinen Augen ist hashable (vom Sinn jetzt mal abgesehen) keine konkrete Klasse aber ebenfalls auch kein konkretes Interface (nocopyable bietet zB garkeine schnittstelle an).
wenn nun hashable den hash wert (warum auch immer) zwischenspeichert, dann habe ich nicht nur codeelemente sondern auch datenelemente in hashable. definitiv kein java interface mehr. fuer mich ist es aber immernoch ein Konzept.
Daß eine Vielzahl an Schlüsselwörtern eine schlechte Sprache ausmacht, ist ein Vorurteil von C- und C++-Programmierern.
Und was wenn ich kein C- oder C++ Programmierer bin?
Es ist eine Erfahrung die ich mit einer Menge Sprachen gemacht habe - das hinzupatchen von features ueber keywords ist meistens unpraktisch.Eine gute Sprache bietet eine kleine Basis anhand derer ich alle moeglichen konstrukte bauen kann. c++ bietet zB sehr doofe keywords wie zB register oder auto. Und vorallem was recht furchtbar ist, ist das wiederverwenden eines keywords in einem komplett anderen kontext. zB static oder typename. Java hat zB ein sehr schoenes set an keywords.
-
byto schrieb:
Shade Of Mine schrieb:
byto schrieb:
@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.
Wie genau komm ich an IFooable und AbstractFoo vorbei?
Du kommst damit um die Vererbung im herkömmlichen Sinne vorbei, also mittels extends. Genau das kann nämlich böse werden und zu unwartbarem Code führen, wenn man irgendwann unüberschaubar viele Vererbungshierarchien hat.
Kapiere ich nicht.
Warum ist es besser von IFooable zu erben und den Code von AbstractFoo::doFoo() zu copy&pasten als direkt von AbstractFoo zu erben? Welchen Vorteil bringt mir das?Mir passiert das hin und wieder und ich aergere mich ueber sowas. Auch dass ich dauernd 2 Klassen (also 1 abstracte klassen und 1 interface) implementieren muss nur um eine defaultimplementierung anbieten zu koennen.
Erklaer mir mal genau warum die Welt untergeht wenn IHashable eine defaultimplementierung fuer hash() anbietet.
Wie ich schon gesagt habe: niemand will von vector, Auto, Haus und string ableiten. Aber ich will von Hashable, nocopyable, Serializable,... erben koennen. Der Punkt ist nur, dass meine Definition eines Konzeptes nicht mit der Definition eines Java Interfaces kompatibel ist. Genau das ist mein Problem.
Ich denke Java Interfaces sind einfach zu restriktiv. Es ist natuerlich super weil man damit gleich verbietet von vector, Auto, Haus und string zu erben - aber es bringt probleme mit sich. Naemlich dass man ploetzlich Code verdopplung hat (man muss jedesmal AbstractFoo und IFooable erstellen) und man macht Copy&Paste vom AbstractFoo Code wenn man IFooable verwenden muss.
Wo ist denn der eigentliche Unterschied zwischen AbstractFoo und IFooable - der Unterschied ist nur technischer Natur dass IFooable keine default implementation anbietet. Es gibt keine logische Trennung. Deshalb sehe ich diese Trennung nicht ein.
-
Shade Of Mine schrieb:
char* p=0;
*p=0;ist auch legal weil es kompiliert - aber so ganz definiert ist das verhalten dann doch nicht.
Dein Vergleich ist inadäquat. Wie ich schrieb, hängt es in meinem Beispiel davon ab, ob und in welcher Reihenfolge Mehrfachvererbung angewandt wird. Für Einfachvererbung oder aber für die erste Basisklasse scheint das sowohl definiert als auch sinnvoll zu sein. Das ist einer der Gründe, der zumindest gegen die C++-Version von Mehrfachvererbung (einigen wir uns doch auf den eindeutigeren Begriff der Mehrfach-Klassenvererbung) spricht.
Shade Of Mine schrieb:
Man tut es nicht und wenn man es tut, dann weil man die konsequenzen kennt.
Ja, genau
Shade Of Mine schrieb:
Weil die Argumentation absurd _ist_. Mehr feature will man immer haben aber _irgendwo_ muss eine grenze gezogen werden. man kann nicht _alles_ haben. ist einfach ein fakt.
Schön, daß du dich in der Lage siehst, objektiv über die Absurdität meines Standpunktes urteilen zu können. Meine Argumente habe ich genannt; jeder bilde sich selbst seine Meinung. Let's agree to not agree.
Shade Of Mine schrieb:
Shade Of Mine schrieb:
der neue c++ standard hat closures. wie das implementiert wird, ist komplett egal.
Nein.
Was genau koennen die C++ lambda ausdruecke nicht was du von einem closure erwartest?
Meine Reaktion bezog sich auf __closure aka Delegates. Entschuldige die Mißverständlichkeit.
Deine Argumente für die Mehrfachvererbung kann ich nachvollziehen. Doch scheint mir ihre Verwendung einerseits keineswegs unabdingbar, und andererseits wiegen die Probleme meiner Auffassung nach wesentlich schwerer.
Shade Of Mine schrieb:
Eine gute Sprache bietet eine kleine Basis anhand derer ich alle moeglichen konstrukte bauen kann. c++ bietet zB sehr doofe keywords wie zB register oder auto. Und vorallem was recht furchtbar ist, ist das wiederverwenden eines keywords in einem komplett anderen kontext. zB static oder typename. Java hat zB ein sehr schoenes set an keywords.
Wie du feststellst, ist es durchaus grausam, ein Schlüsselwort kontextabhängig unterschiedlich zu interpretieren - so wie C++ das, um die Einführung eines neuen Schlüsselwortes zu vermeiden, mit äußerster Verzweiflung tut. In Sprachen wie Delphi oder meinetwegen auch Java finde ich das viel entspannter.
-
audacia schrieb:
...ist es durchaus grausam, ein Schlüsselwort kontextabhängig unterschiedlich zu interpretieren - so wie C++ das, um die Einführung eines neuen Schlüsselwortes zu vermeiden, mit äußerster Verzweiflung tut. In Sprachen wie Delphi oder meinetwegen auch Java finde ich das viel entspannter.
java kriegt's sogar hin, keywords trotz mehrfacher verwendung (final) so einzusetzen, dass die bedeutung so ähnlich ist, dass jeder es sofort versteht, wenn er nur einen fall kennt. das ist bei C (extern und static z.b.) weniger gut gelungen.
-
audacia schrieb:
camper schrieb:
Gegenfrage: warum sollte man sich überhaupt irgendwelche Gedanken über Adressen machen müssen?
Schreibt der Standard vor, daß static_cast <Base*> (reinterpret_cast <Derived*> (0)) == 0 ist, auch wenn Base nicht am Beginn des Objektlayouts von Derived liegt? Falls nicht, müßte man p ? static_cast <Base*> (p) : 0 schreiben.
Der Standard schreibt hinsichtlich reinterpret_cast so gut wie überhaupt nichts vor, sofern die betreffenden Typen (wie in unserm Falle) keine PODs sind und auch nicht union-Member sein können. Das trifft so auch auf einfache Vererbung zu. Ich wiedeehole meine Frage: warum sollte man so etwas tun wollen? Praktisch alle Szenarien, in denen man das brauchen könnte, sind noch aus anderen Gründen undefiniert. Und ein Sprachfeature (Mehrfachvererbung) für schlecht zu halten, weil damit bestimmte andere furchtbare Sachen nicht richtig funktionieren, ist nicht sehr überzeugend.
Ein anderer, wesentlich unangenehmerer Aspekt im Zusammenhang mit Methodenzeigern: seltsamerweise darf (oder wenigstens: sollte) man einem Methodenzeiger für eine Basisklasse keine Methode einer abgeleiteten Klasse zuweisen. Tut man es doch (oder erzwingt es mittels Cast), so passieren haarsträubende Dinge:
class Base { protected: int i; public: Base (void) : i (42) {} int func(void) { return i; }; }; typedef int (Base::* basemember_t) (void); class Base2 { int j; public: Base2 (void) : j (1337) {} }; class Derived : public Base2, public Base { public: int new_func(void) { return i; }; }; void foo (void) { Derived d; int i; basemember_t m = static_cast <basemember_t> (&Derived::new_func); i = (d.*m) (); }
Dies scheint legales C++ zu sein, jedenfalls kompilieren Comeau, MSVC und BCC das anstandslos (MSVC ist wenigstens so entgegenkommend und warnt vor möglichen Konsequenzen). Ob aber der Wert von i nun erwartungsgemäß 42 oder aber undefiniert ist, hängt schlicht davon ab, in welcher Reihefolge die Basisklassen von Derived genannt werden.
Falls der Compiler alles richtig macht, spielt das Layout keine Rolle - dieser Code hat in jedem Falle wohldefiniertes Verhalten. Wir können gerne diskutieren, dass die Compilerunterstützung hier möglicherweise nicht immer genügt. Aber dann sollte deine Argumentation auch darauf abzielen: dass dieses Sprachfeatures zu komplex wäre, um mit vertretbarem Aufwand korrekt implementiert zu werden - ich meine aber aber, das die Verbreitung dieses Features eher gegen diese These spricht.
Falls du meinst, so etwas tue man doch nicht: Erstens wird fast alles, was der Compiler erlaubt, auch getan, und zweitens tut Microsoft das höchstselbst in den MFC.
Ja, ich bestreite nicht, dass in der Praxis viele seltsame Dinge gemacht werden. Ich sehe trotzdem nicht, wie das deine Position hinsichtlich des diskutierten Sprachfeatures unterstützt.
-
Shade Of Mine ich muss dir aber wiedersprechen. Wo hast du in Java viele Vererbungen? Ich habe hier ein 10.000 Line Projekt, und fast gar keine Vererbungen. Wieso erbst du von JFrame?
class MeinFenster extends JFrame
besser ist doch:
class MeinFenster { private JFrame frame; }
Ich wuerde von JFrame nur dann erben, wenn ich die protected-Methoden von JFrame brauche. Also wenn ich das Verhalten eines JFrame aendern, erweitern moechte. Kurz: Wenn ich mein eigenes veraenderes JFrame brauche. Aber sowas Spezielles brauchst du doch nie.
Dann default-Implementierung von Interfaces, wozu? Was bitte soll ein Seriazable default machen? Oder ein Cloneable? Oder ein MouseListener?
In der SDK hast du abstract class MouseAdapter implements MouseListener, MouseMover, MouseWheel aber das nur um Tipparbeit zu sparen. In MouseAdapter machen alle "geerbten" Methoden nichts, aber du brauchst nicht alle Methoden von z.B. MouseListener hinzuschreiben, wenn du nur mouseClick() brauchst.
In Java hast du viele Objekte, ich benuzte sehr viele annonyme Klassen. Meine Hauptklassen erben meisten nichts und implementieren nichts, damit ich die Vererbungshirarhie so einfach wie moeglich habe.