Als Java-Entwickler noch C/C++ dazulernen: Chancenlos?



  • Neil deGrasse Tyson schrieb:

    The good thing about science is that it's true whether or not you believe in it.



  • Alles eine Frage der Perspektive.

    Nein, dein Fahrradbeispiel ist falsch.

    Falsch ist aber keins davon, denn es kommt das gleiche raus

    Die Richtigkeit am Ergebnis messen, ist ebenfalls falsch und missachtet die Logik.



  • knivil schrieb:

    Na das hat doch jetzt eher was mit Optimierung leerer Basisklassen zu tun. Trotzdem ist fuer mich Vererbung der Ausnahmefall und der eingeschlagene Weg von Xin fuer falsch. Es gibt genug Argumente und Beispiele im Internet, rueckblickend finde ich meinen Verzicht auf Vererbung als gut.

    Denk ich nicht, für die Optimierung eine nicht genutzten Aggregation/Komposition ist mindestens eine Whole program optimization notwendig, die dann die binär Kompatibilität brechen würde. Stelle sich einer vor, was passiert, wenn man Objekte mit der optimierte Funktion und danach unoptimierte Funktion verwendet. IMO wird das richtiger Datenchaos.
    Übrings ist die Ausgabe von GCC identisch.



  • knivil schrieb:

    Alles eine Frage der Perspektive.

    Nein, dein Fahrradbeispiel ist falsch.

    Na, dann freue ich mich auf eine spannende Mail von Dir. 🙂

    knivil schrieb:

    Falsch ist aber keins davon, denn es kommt das gleiche raus

    Die Richtigkeit am Ergebnis messen, ist ebenfalls falsch und missachtet die Logik.

    Always listen to experts.
    They tell you what can't be done and why.

    Then do it.
    (Colin Plumb, wenn ich mich recht entsinne)

    Ich denke, ich bewege mich durchaus in den Grenzen der Logik. Zumindest programmiere ich so seit einigen Jahren, forciere das auch und komme damit sehr gut voran.

    Es gibt hier einen klaren Unterschied: Während ich ein sowohl als auch akzeptiere und beide Methoden gerne nutze, wie sie sich als praktisch erweisen, legst Du Dich auf eins fest.
    Wenn Dir das lieber ist, ist das für mich in Ordnung. Aber es wäre schon schön, wenn Du mir entweder irgendwas Inspirierendes mitgeben kannst oder Deine Behauptungen vielleicht argumentativ etwas untermauen könntest. (Mail?)

    Ansonsten danke ich Dir für die informative Wiederholung bekannter Dogmen und kann weiterhin alles "falsch" machen.



  • Ist dir eigentlich schonmal der Gedanke gekommen, dass du möglicherweise deinem eigenen Dogma unterliegst?



  • dot schrieb:

    Ist dir eigentlich schonmal der Gedanke gekommen, dass du möglicherweise deinem eigenen Dogma unterliegst?

    Ich unterliege vermutlich vielen Dogmen. Es fiel mir erstmals auf, als man mir Java beibrachte und mir ganz selbstverständlich Dinge erklärte, die ich für absoluten Schwachsinn halte und die Profs ausquetschte und die auch meinten, dass ich falsch liege - aber auch keine Erklärung hatten, warum ich falsch liege.

    Die Vermeidung von Mehrfachvererbung ist eins solches Dogma, dem ich nicht mehr unterliege. Übrigens nicht wegen Java, sondern dank diesem Forums, wo man mir dogmatisch erklärte, dass Mehrfachvererbung böse ist und ich das so nicht einsah. Also argumentierte ich dagegen. Damals benutzte ich Mehrfachvererbung aber auch nicht so häufig wie heute. Aber mir fiel in der Diskussion auf, dass das eigentlich ein geiles Feature ist, dem ich viel zu wenig Beachtung geschenkt habe. Da fiel mir auf, wie blödsinnig dieses Dogma ist und ich begann damit rumzuexperimentieren.

    Sehr praktisches Feature. 🙂

    Natürlich unterliege ich Dogmen. Wenn ich an meinem Compiler schreibe und nicht daran gebunden bin, was die Sprache mir offenbar vorgibt, kann ich auch mit Konstrukten spielen, die in C++ nicht üblich sind. Und auf einmal fällt einem auf, dass man ganz selbstverständlich Dinge hinnimmt, obwohl es eigentlich keinen Grund dafür gibt. Es muss nur einer irgendwann mal für wichtig empfunden worden sein und der hat was gesagt und die Welt käut es wieder.

    Sehr schön fand ich die Begründung, warum C# kein Const-Correctness unterstützt. Warum das im Design fehlt, hat der C#-Entwickler beschrieben. Und wenn der das sagt, dann ist vollkommen klar, dass ich als Const-Correctness-Fanatiker ja wohl nur ein Spinner bin. Der C#-Entwickler hat das ja wohl klar begründet, dass Const-Correctness Schwachsinn ist.

    Und hier bin ich halt der Spinner mit der Mehrfachvererbung.



  • a => b, b (das Ergebnis) ist wahr, leider kann ich nichts ueber a sagen.

    Ein Fahrrad hat zwei Räder. Ein Fahrrad ist ein Zweirad.

    Hier stellst du die Schlussfolgerung an den Anfang. Richtig ist: Ein Zweirad hat 2 Raeder und ein Fahrrad ist ein Zweirad. Daraus folgt ein Fahrrad hat 2 Raeder. Einfache Deduktion aus Praemisse + Gesetz.

    Mathematisch: has_a-Beziehung entspricht Praedikaten und is_a-Beziehung entspricht der Teilmengenbeziehung. Obiges Beispiel: Zweirad ={ x | hat_zwei_raeder(x) }, Fahrrad = { y \in Zweirad }.

    Genau das steht auch bei Wikipedia:

    Liskovsches Substitutionsprinzip schrieb:

    Sei q(x) eine beweisbare Eigenschaft von Objekten x des Typs T. Dann soll q(y) für Objekte y des Typs S wahr sein, wobei S ein Untertyp von T ist.

    Fuer dich: q = hat_zwei_raeder, T = Zweirad, S = Fahrad.

    Das ist Objektorientierung, nix Dogma nur Mathe. Dabei ist der Zugriffsspezifikator public, private oder protected egal (auch wenn es funktioniert, siehe oben). Was du machst ist keine Objektorientierung im Sinne des Erfinders.

    Und hier bin ich halt der Spinner mit der Mehrfachvererbung.

    Ich kann auch von Tuer zu Tuer rennen und gegenkacken. Dann bin ich auch ein Spinner. Aber mit Logik hat das nichts zu tun und jeder bei klarem Verstand wuerde sich dafuer schaemen.

    Alle Argumentation zum Trotz denkst du, richtig zu handeln. Deutlich: Nein! Deine Behauptungen sind ignorant und dumm. Beratungsresitent!

    dass man ganz selbstverständlich Dinge hinnimmt, obwohl es eigentlich keinen Grund dafür gibt.

    Na weil ich auch nicht 1 + 1 = 2 (natuerliche Zahlen) staendig hinterfrage. Einmal reicht voellig.

    Wenn Vererbung der Teilmengenbeziehung entspricht, dann entspricht Mehrfachvererbung dem Schnitt von (Teil)Mengen. Aber wenn er nicht leer ist, dann wurden zwei Typen/Mengen definiert, die nicht disjunkt sind. Bzw. nicht orthogonal... Schlechtes Design. Du hingegen benutzt Mehrfachvererbung als Vereinigung von Mengen, was eben nicht dem zugrundeliegendem Prinzip entspricht.

    ich das so nicht einsah

    Man ich koennte fast jeden Teilsatz einfach zerreissen. Mathe/Logik ist einfach ... unlogisch?



  • Abgesehen davon passt es hinten und vorne nicht.
    Fangen wir mal an,

    Dokument erbt von Filename
    OpenFileDialog erbt auch von Filename
    BitmapDocument erbt von Document und Bitmap
    Application erbt von Hauptmenu und Dokument und Hauptfenster
    Das Hauptfenster erbt von Window, OKButton, CancelButton, Rahmen, ClientArea

    Schon kann eine Application nur ein Dokument haben.
    Das muss natürlich weggetricksat werden, schlage template<size_t number,typename T> TReingeerbtesAttribut vor, dann kann man mehrere gleichartige reingeerbte Attribute wenigstens anhand einer Nummer ausenanderhalten. char wäre noch besser, weil viel lesbarere und Variablennamen mit mehr als einem Buchstaben hat man noch nie gebraucht.

    Die Application hat auch einen Dateinamen, also erbst sie direkt einen Dateinamen und indirekt noch einen über das Dokument.

    Da muß man doch merken, daß der Ansatz kaputt ist.

    Und das macht muir Angst, was ist, wenn man dabei tatsächlich mal Vererbung ausdrücken möchte? Die geht in dem ganzen Müll ja total unter.



  • Ohne hier mitdiskutieren zu wollen, da das eh sinnlos ist, nur ein kleiner Hinweis:

    mixins

    Der erste Schritt ist natuerlich Mixins mit Mehrfachvererbung zu implementieren, weil es simpel wirkt. Das hat sich aber als nicht so toll rausgestellt und deshalb macht man es lieber so: http://www.drdobbs.com/cpp/mixin-based-programming-in-c/184404445 (mit Templates)

    Das ganze ist auch schon wieder 12 Jahre her.



  • knivil schrieb:

    Was du machst ist keine Objektorientierung im Sinne des Erfinders.

    Das habe zum einen auch nie behauptet und zweitens kannst Du die Aussage genauso wenig belegen, wie ich die (nie getroffene) Aussage, dass es im Sinne des Erfinders ist.
    Dafür müssten wir erstmal rausfinden, wer das Design Pattern der objektorientierten Programmierung erfunden hat. Mich interessiert auch nicht die Intention irgendeines Erfinders, der sitzt schließlich nicht vor meinem Quelltext.
    Interessanterweise hat der Erfinder aber auch private und protected-Ableitungen in C++ vorgesehen. Die Privaten Ableitungen nutze ich nicht, weil ich dann auch klar rausstellen möchte, dass eine Variable abgeschirmt ist.
    Da Du den Erfinder von OOP so gut kennst, würde mich mal dessen Intention dazu interessieren.

    OOP löst ein Problem. Ich löse Probleme. Und um Probleme zu lösen, kann man sich einschränken lassen oder nicht. Und wenn ich Dinge anders modellieren möchte als Du oder Volkard das für richtig halten, so ist das nicht gleichbedeutend mit 'falsch', es entspricht lediglich nicht dem Lehrbuch.
    Es hat sich aber für mich als praktisch erwiesen. Das ist erwähnenswert und genausowenig falsch, wie OOP zu Zeiten, als OOP noch ein synonym für Unsinn war.
    Ich will kein Lehrbuch schreiben, ich will Probleme lösen.

    Liskovsches Substitutionsprinzip... sicher, dass Du das als Argument gegen Mehrfachvererbung anbringen möchtest?

    Lieber Knivel, wie ich Deinen Wunsch nach Lehrbuch-Programmierung respektiere und dagegen nicht argumentiere, weil ich genauso gut weiß, dass das auch funktioniert, wäre es durchaus nicht zuviel verlangt, wenn Du akzeptierst, dass das Lehrbuch nicht für alle Menschen eine Bibel darstellt und andere Menschen zum Teil andere Wege gehen und damit gut klarkommen.

    volkard schrieb:

    Abgesehen davon passt es hinten und vorne nicht.
    Fangen wir mal an,

    Dokument erbt von Filename
    OpenFileDialog erbt auch von Filename
    BitmapDocument erbt von Document und Bitmap
    Application erbt von Hauptmenu und Dokument und Hauptfenster
    Das Hauptfenster erbt von Window, OKButton, CancelButton, Rahmen, ClientArea

    Schon kann eine Application nur ein Dokument haben.

    Dein Posting zeigt zwei Dinge: Erstens, dass Du Dir Dinge zusammengefasst hast, die hier nicht geschrieben wurden, die in Deinen Augen nicht passen. Was hat das mit mir zu tun!? Woher kommt eigentlich OpenFileDialog? Warum sollte Application von Document erben? Das würde nur Sinn ergeben, wenn die Applikation selbst als Dokument aufträte, es also eine Applikation für Applikationen gabe.

    Zweitens: In dem Du Dinge falsch zusammenbaust, baust Du Dir kein Argument auf. Du kannst Application von einer Liste von Dokumenten erben lassen, schon ist das semantisch vollkommen in Ordnung.
    Dir ist vor allem ein wichtiger Punkt entgangen, den ich schon beschrieb: Wenn Du eine Architektur über Mehrfachvererbung aufbaust, musst Du deutlich präziser typisieren. Das hast du bei den Buttons schon gemacht: Sie sind unterscheidbar.

    Ich versuche es erneut und ich arbeite mit Beispielen(!). Beispiel heißt einfaches und für ein Posting kleines Anschauungsobjekt, nicht zwangsläufig der perfekte Fall, aber geeignet um eine Idee zu transportieren, wenn man sich für die Idee interessiert und statt sofort versucht, alles abzustreiten erst die Möglichkeit akzeptiert, dass es eine Idee sein könnte.

    Nehmen wir eine Linie. Eine Linie kann man modellieren als ein Objekt, das zwei Punkte hat. Wir definieren also Punkt und Linie, packen zwei Punkte in die Klasse Linie, die wir mit Startpunkt und Endpunkt ansprechen und fertig.

    Mache ich das über Mehrfachvererbung, so muss ich mehr definieren: Ich kann ja nicht zweimal von Punkt ableiten. Ich muss also Typen definieren, die ich von Punkt ableite, aber unterscheidbar sind: Startpunkt und Endpunkt. Herzlich Willkommen im Diamanten. Es ist aber nicht das Diamant-Problem, es ist das Diamant-Feature. Diamant-Feature? Schonmal gehört? Steht in keinem Lehrbuch drin, weil der Diamant ist was böses in der Programmierung. So etwas darf man nicht als Feature beschreiben. Warum darf ich die logischen Eigenschaften denn eigentlich nicht nutzen? Weil in einem Lehrbuch steht, dass der Diamant Böse ist? Ist hier schon einer vom Wichsen blind geworden, oder was?

    Also Diamant-Feature.

    Welchen Sinn macht das Ganze? Ich muss nun "umständlicher" auf die Koordinaten zugreifen:

    line.Startpunkt::x
    
    statt
    
    line.Startpunkt.x
    

    Ist jetzt nicht der riesen Unterschied, oder? Es passiert intern auch nichts anderes, es ist ja auch nichts anderes: Es ist eine Aggregation, die über Mehrfachvererbung modelliert wurde.
    Ich wurde lediglich genötigt, den Membernamen über Typnamen zu modellieren.

    Ich kann nun aber Funktionen schreiben, die sich ausschließlich mit dem Startpunkt ODER mit dem Endpunkt beschäftigen und das auch in der Signatur zum Ausdruck bringen. Ich muss jetzt auch nicht aufpassen, dass ich die beiden Punkte versehentlich vertausche, denn sie haben unterscheidbare Datentypen. Die Information, ob ich mit einem Startpunkt oder Endpunkt arbeite, kann ich viel länger erhalten und bei einer Funktion, die einen Startpunkt verlangt, muss ich nicht darauf hoffen, dass der Entwickler auch wirklich line.Startpunkt reinwirft, er wirft einfach das komplette Objekt rein und der Compiler greift das richtige Objekt raus. Als Entwickler bin ich überhaupt nicht mehr in der Lage, den falschen Punkt in die Funktion zu werfen (allerdings müssen Konstruktoren ggfs. explicit sein).

    Ich sehe das als Vorteil. Das ist von C++ nicht unbedingt optimal unterstützt, es ist schließlich kein Weg aus dem Lehrbuch. Oder umgekehrt: Vielleicht ist das Lehrbuch auch einfach der üblichen Benutzung von Strukturen in der Historie der Programmiersprachen angelehnt. Wäre auch eine Möglichkeit. Wurde das erste Lehrbuch zur OOP eigentlich geschrieben, bevor der erste OOP gemacht hat oder wurde erst gegen gängige Programmierrichtlinien verstoßen und dann ein Buch geschrieben!?
    Ein Lehrbuch ist ein Aggregation von Papier, seine Existenz ist kein Beweis, dass da der Weisheit letzter Schluss drinsteht - oder überhaupt irgendetwas sinnvolles. Man darf Lehrbücher und Lehrer in Frage stellen.

    Aber was ich tue ist modellierbar und man darf sich fragen, was diese Form der Modellierung ändert und ob das in manchen Situationen Vorteile bringt. Es funktioniert. Oder man klammert sich mit Händen und Füßen daran, dass eine andere Perspektive nicht sein darf, wie Knivils es zeigt, weil man ausschließlich von seinem Standpunkt aus auf Fahrräder gucken darf und er glaubt, weil er ein paar mathematische Symbole um seine Worte packt, dass das bedeutet, dass eine andere Perspektive nicht in mathematische Symbole zu kleiden wäre.

    ∀(Zweirad) : Zweirad ∈ { FahrzeugMitVorderrad } ∧ Zweirad ∈ { FahrzeugMitHinterrad }

    Sowas ist doch keine konstruktive Diskussion!? Das ist Schwanzvergleich. Schäufelchenklauen im Sandkasten. Kurz: Kinderkacke. Langweilig.

    Ich finde diese Form der Modellierung in vielen Fällen praktisch, ausbaufähig und sehe Potential darin. Und ich werde mich weiter damit beschäftigen. Fertig.

    Ich habe mich nie gegen die Programmierung mit Membern ausgesprochen oder gar geschrieben, dass ich zum Programmieren nichts anderes mehr tue als ausschließlich abzuleiten.
    Aber ich nutze auch diese Möglichkeit meine Datenstrukturen zu modellieren und wenn ihr Argumente habt, die dagegen sprechen, dann bin ich interessiert die zu hören. Knivils "Das ist falsch" ohne weiteren Kommentar oder sich Ableitungshierarchien auszudenken und darauf basierend Schlussfolgerungen zu ziehen, dass es bei mir vorne und hinten nicht passt, ist keine Argumentation.
    Und eine Meinungsverschiedenheit ist akzeptabel, wenn beide Seiten die Toleranz besitzen, das hinzunehmen.
    Vielleicht verabschiede ich mich irgendwann von meiner heutigen Ansicht, vielleicht ihr. Das kann man doch so stehen lassen, wenn man sich nicht einig wird, oder?



  • Wo ist bei eine ungerichtet Linie der Startpunkt und wo der Endpunkt?



  • 😮



  • Mich interessiert auch nicht die Intention irgendeines Erfinders

    Und den Matheteil hast du ueberlesen?

    private und protected-Ableitungen in C++ vorgesehen

    Ergaenzungen, sie sind fuer das Grundkonzept nicht noetig.

    so ist das nicht gleichbedeutend mit 'falsch'

    Ja, du hast den Matheteil uberlesen.

    als OOP noch ein synonym für Unsinn war

    Nenne mir den Zeitpunkt, wann das so war.

    Liskovsches Substitutionsprinzip... sicher, dass Du das als Argument gegen Mehrfachvererbung anbringen möchtest?

    Du hast nicht verstanden. Substitutionsprinzip beschreibt die Mathematik. So wie du Mehrfachvererbung benutzt entspricht es nicht den Regeln der Mathematik. Au weia Dogma ... boese.

    für alle Menschen eine Bibel darstellt

    Ich halte Vernunft bei vernuenftigen Menschen fuer zwingend. Du stellst es als Religion dar und willst mich in die Fanatikerschiene druecken.

    andere Menschen zum Teil andere Wege gehen und damit gut klarkommen

    Klar kommt man ohne Vernunft klar, wenn man allein und abgeschieden lebt. Dort wird kein Mathe gebraucht wird. Aber Programmierer sollte man trotzdem nicht werden.

    Deine Argumentation baut darauf auf, dass nur Lehrbuchmeinung wiedergekaeut wird. Dem ist nicht so. Einfach mal die Orginale lesen und versuchen zu verstehen. Als ob du der einzige selbstdenkende hier bist.

    Was denkst du denn. Wenn heute jemand noch mit dem Bohrschen Atommodel die Physik betreibt so wird er hoechstens muede belaechelt. Klar kan er viele Ergebnisse produzieren, aber sie beruhen auf einem falschen Model. Niemand macht sich die Muehe zuzuhoeren oder gar die Veroeffentlichungen zu lesen. Sie werden schlicht ignoriert.



  • Es gibt scheinbar schon wirklich Gründe warum MI problematisch ist, z.B. das hier: http://stackoverflow.com/questions/225929/what-is-the-exact-problem-with-multiple-inheritance Es gibt als Alternative zu MI auch die Traits in Scala. Die sind allerdings auch nicht unproblematisch, da sie stateful sind. Damit spielt Reihenfolge der Initialisierung ebenfalls eine Rolle wie bei MI, siehe http://blog.jetbrains.com/kotlin/2011/08/multiple-inheritance-part-2-possible-directions/ Ich finde die Mixins ind D sind eine sehr schöne Lösung: http://dlang.org/template-mixin.html Nur schade, dass D so wenig Wiederhall findet.

    Okay, zurück zum eigentlichen Thema. Ich bin nämlich der, der den Thread eröffnet hat... Aber lasst euch von eurer Diskussion über "to MI or not to MI" nicht stören ;-).

    Also ich habe im Studium mal 2 Semesterarbeiten mit C++ gemacht und auch meine Diplomarbeit. Ich bin also so auf fortgeschrittenem Anfängerniveau bis unterem mittlerem Niveau. Ich denke das Problem dürfte eher plain C sein. C-Code kann manchmal ziemlich verworren sein. Wir haben eine ältere C-Anwendung bei uns im Geschäft in Produktion. Da werde ich mir den Code holen und den versuchen zu verstehen. Das dürfte mir auf die Sprünge helfen den normal üblen C-Code zu verstehen. Was C++ selbst angeht, denke ich, liegt die Hauptschwierigkeit in der Speicherverwaltung. Auch wenn RAII vieles einfacher macht, ist es doch nicht alles und muss auch erst verstanden werden ...

    Also dann schau ich mal. Wollte schon immer mal C/C++ machen. Man muss einfach seine eigene virtuelle Maschine in C/C++ schreiben können :-))). Dachte aber dass es mittlerweile chancenlos ist. Aber es gibt doch nicht so wenige Stellenbeschreibungen für Java-Entwickler, in denen steht, das Kenntnisse in C++ ein Plus sind. Dann wünscht mir Glück :-).

    Cheers, Saxo



  • Saxo schrieb:

    Ich denke das Problem dürfte eher plain C sein.

    Jo. Inzwischen sind die Sprachen eben viel weiter.

    Saxo schrieb:

    Was C++ selbst angeht, denke ich, liegt die Hauptschwierigkeit in der Speicherverwaltung.

    Nö. Verwende einfach kein new/delete, ohne vorher hier zu fragen.

    Saxo schrieb:

    Wollte schon immer mal C/C++ machen.

    Das ist wirklich noch schlechter als C. Finger weg von C/C++-Büchern.

    Saxo schrieb:

    Man muss einfach seine eigene virtuelle Maschine in C/C++ schreiben können :-))).

    Nö. Man muß bei weitem nicht alle C++-Tricks können, um sehr gute Programme schreiben zu könnne. Hier führt man gelegentlich vor, wie man mit fünf Schachfiguren jongliert, einen Schachfigur übers Haus wirft, aus den Buchstaben S,C,H,A,C,H,F,I,G,U,R das Wort GRAFISCH bauen kann, und in der Tat, wer so begeistert dabei ist, dass er echt gar nix anderes mehr im Kopf hat, ist bei sonst vergleichbaren Umständen oft auch der bessere Schachspieler.



  • Saxo schrieb:

    Es gibt scheinbar schon wirklich Gründe warum MI problematisch ist

    Die Frage ist halt, ob man versteht, was man da beschreibt oder nicht.
    Problematisch ist entsprechend, wenn man mit Mehrfachvererbung Dinge macht, die man nicht (richtig) versteht.

    Saxo schrieb:

    Okay, zurück zum eigentlichen Thema. Ich bin nämlich der, der den Thread eröffnet hat... Aber lasst euch von eurer Diskussion über "to MI or not to MI" nicht stören ;-).

    Die Diskussion hängt sich an mir auf, weniger an der Mehrfachvererbung. Das ganze funktioniert nicht, wenn ich nicht dagegen halte.

    Saxo schrieb:

    Also ich habe im Studium mal 2 Semesterarbeiten mit C++ gemacht und auch meine Diplomarbeit. Ich bin also so auf fortgeschrittenem Anfängerniveau bis unterem mittlerem Niveau. Ich denke das Problem dürfte eher plain C sein. C-Code kann manchmal ziemlich verworren sein. Wir haben eine ältere C-Anwendung bei uns im Geschäft in Produktion. Da werde ich mir den Code holen und den versuchen zu verstehen. Das dürfte mir auf die Sprünge helfen den normal üblen C-Code zu verstehen. Was C++ selbst angeht, denke ich, liegt die Hauptschwierigkeit in der Speicherverwaltung. Auch wenn RAII vieles einfacher macht, ist es doch nicht alles und muss auch erst verstanden werden ...

    Lass Dir von volkard keine Mist erzählen. RAII ist eine wunderbare Technik, die Dir hilft Probleme zu vermeiden. Und ich stimme volkard absolut zu, dass RAII (fast?) immer die richtige Antwort ist, wenn man qualitative C++-Standard-Anwendungen schreiben möchte. Manchmal will man aber eben keine Standard-Anwendungen schreiben und dann ist es auch nicht verkehrt, wenn man bei (void 😉 nicht gleich Spastiken entwickelt.

    RAII ist aber weder - wie er andeutet - die einzige Antwort, noch musst Du Dir irgendwo Erlaubnis holen, wenn Du new oder delete verwendest. Ich denke, die volkards Aufforderung hier sagt genug über seine Position aus, um diese angemessen einschätzen zu können.

    Saxo schrieb:

    Also dann schau ich mal. Wollte schon immer mal C/C++ machen. Man muss einfach seine eigene virtuelle Maschine in C/C++ schreiben können :-))). Dachte aber dass es mittlerweile chancenlos ist. Aber es gibt doch nicht so wenige Stellenbeschreibungen für Java-Entwickler, in denen steht, das Kenntnisse in C++ ein Plus sind. Dann wünscht mir Glück :-).

    Es ist die Frage, wieviel Zeit Du damit verbringen willst und kannst. Geh es locker an, spiele damit. Programmieren kann Spaß machen, ein Job sollte Spaß machen und es ist zur Weiterbildung durchaus nützlich selbst Erfahrungen zu sammeln, wo z.B. RAII hilfreich ist, statt RAII einfach anzuwenden, ohne zu wissen weshalb. Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.
    Dinge (meistens) richtig zu machen, ohne zu begreifen was man da tut, bedeutet nicht, dass man C++ programmieren kann.

    Und wenn Du Spaß an einer VM hast... ganz ehrlich... eine kleine VM ist vergleichsweise schnell geschrieben. Aufwendiger finde ich die Daten für die VM zu produzieren. Wenn das ein Ziel ist, an dem Du Spaß hast, Du kannst viel damit spielen, experimentieren und somit auch viel über C++ lernen.

    Viel Spaß 🙂



  • Xin schrieb:

    Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.

    🙄 Abgesehen davon würde ich mir wünschen du könntest es auch so darstellen.



  • Zeus schrieb:

    Xin schrieb:

    Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.

    🙄 Abgesehen davon würde ich mir wünschen du könntest es auch so darstellen.

    Du kannst die Augen verdrehen, wie Du magst, aber ich habe aber kein Problem mit dem Diamanten. Ich kann den Diamanten nicht als Problem darstellen, wenn ich ihn auch nutzen kann. Der Diamant ist - wie jede Programmiertechnik - etwas was Vor- und Nachteile besitzt und damit muss man sich auseinander setzen.
    Genauso wie Listen Vor- und Nachteile gegenüber Arrays haben und man eben nicht uneingeschränkt das eine empfehlen kann, weil man die Nachteil des anderen nicht mag.

    Mehrfachvererbung kann durchaus so verwendet werden, dass überhaupt keine Diamant-Konstruktion entsteht. Also wenn der Diamant für Dich ein Problem darstellt, so gibt es immernoch keinen Grund, sich gegen die Mehrfachvererbung auszusprechen. Sprich Dich dann lieber dafür aus, bei Mehrfachvererbung auf gemeinsame Basisklassen zu verzichten, um die versehentliche Konstruktion eines Diamanten zu verhindern.

    Einige User in diesem Forum sind verhältnismäßig enttäuschend hier unterwegs: Als ob der eigene Wunsch maßgeblich für den Rest der Welt wäre oder volkards Aufforderung hier nachzufragen, ob man new oder delete verwenden darf. Was ist das hier - Inquisition?
    Akzeptiert bitte auch, dass andere Entwickler andere Erfahrungen machen und jeder das Recht hat, seine Erfahrungen zu machen und daraus die Schlüsse zu ziehen, die jedem erlauben, seine Probleme so qualitativ wie möglich zu arbeiten.
    Ein Forum sollte dazu dienen, über diese Schlüsse zu beraten, Hilfestellung oder Inspiration zu geben, sich auszutauschen.

    Aber weder hat hier einer das Recht, Erklärungen einzufordern, noch Vorgaben zu einer Problemlösung machen, schonmal gar nicht, wenn das Problem überhaupt nicht bekannt ist oder gar sich bzw. das Forum als die entscheidende Instanz für anderer Leute Programmierdesign darzustellen. Genausowenig besteht das Recht, erst die Augen zu verdrehen und dazu Wünsche in dieser Form zu äußern.

    Darüber bitte mal ganz unabhängig von C++ mal nachdenken.



  • Xin schrieb:

    marco.b schrieb:

    Xin schrieb:

    ich spielte durchaus schon mit dem Gedanken, Aggregationen komplett abzuschaffen: weniger Boilerplate und zwangsläufig scharfe Typdefinitionen. Das will man doch!?

    Wenn ich richtig verstanden habe, wie du Vererbung einsetzt, koppelst du aber auch stärker. Stichwort als Beispiel: Inversion of Control.

    Wieso kopple ich stärker?

    Nehmen wir an, du hast eine abstrakte Basisklasse "Log", welche lediglich rein virtuelle Methoden besitzt, etwa log(int code, string message).
    Nun gibt es die Subtypen
    SystemEventLog : Log
    FileLog : Log
    TcpLog : Log
    welche alle ein Anwendungsprotokoll hin zu verschiedenen Datenquellen implementieren.

    Wenn du nun keine Aggregation/Komposition sondern nur Vererbung verwendest und du in Klasse Worker ein Protokoll benötigst, hast du zwei Möglichkeiten:

    a)

    class Worker : Log { ... }
    

    Wäre rein logisch offenbar falsch. Schließlich willst du kein Log implementieren, sondern eins verwenden. Ferner kannst du obige Implementierungen nicht wiederverwenden.

    b)

    class Worker: FileLog
    

    Also das Erben von einer konkreten, implementierenden Klasse. Damit hast du aber eine fest Abhängigkeit gekoppelt.
    Mit Aggregation könntest du dagegen einen Member vom Typ Log deklarieren, die konkrete Implementierung aber durch Polymorphie-Zauber dynamisch auflösen, etwa weil du Protokollkanäle dynamisch konfigurierbar halten willst. In Java/C# werden solche Aufgaben üblicherweise mit Dependency Injection Frameworks gelöst. Das klappt aber bei deiner Vererberei nicht.



  • Moin marco!

    Danke für den erklärenden, nicht abfälligen Tonfall. So macht der Austausch jedenfalls Spaß.

    marco.b schrieb:

    Mit Aggregation könntest du dagegen einen Member vom Typ Log deklarieren, die konkrete Implementierung aber durch Polymorphie-Zauber dynamisch auflösen, etwa weil du Protokollkanäle dynamisch konfigurierbar halten willst. In Java/C# werden solche Aufgaben üblicherweise mit Dependency Injection Frameworks gelöst. Das klappt aber bei deiner Vererberei nicht.

    Eine schöne Problemstellung, für die ich sogar bevorzugt Vererbung wählen würde!

    Ich kann in C++ keinen Member vom Typ Log deklarieren, da Log ja eine abstrakte Basisklasse ist.

    Das geht in Java, aber da wird ein "Referenztyp" auch als "Log " definiert. Entsprechend muss ich ebenfalls einen (Log) nehmen, bzw. wenn ich das ausschließlich zur Initialisierungszeit für immer festlegen möchte, dann habe ich in C++ auch die Möglichkeite ein (Log &) zu nehmen. Damit wäre die Kopplung frei bis zur Initialisierung. Das würde aber nicht der Java-Referenz entsprechen und warum sollte ich mich festlegen, Logger niemals wechseln zu können!?

    Möchte ich das Problem wie von Dir beschrieben lösen, würde ich einen LogForwarder definieren, den ich nicht von Log abgeleiten würde, aber inline das gleiche Interface besitzt und der einen Member vom Typ (Log 😉 besitzt. Nochmal: Dass ich mehr Ableitungen verwende, bedeutet nicht, dass ausschließlich ableite und ich keine Member verwende! Ich verwende sie vor allem da, wo ich sie verstecken will. Hier muss ich einen Member nehmen, weil ich in C++ ja gar nicht von (Log 😉 ableiten kann. Ich kann diese Funktionalität in C++ also anders gar nicht formulieren.

    Das ist - wie ich schon sagte - mehr Arbeit, um die Typen zu definieren, hat aber den Vorteil (und deswegen würde ich es so implementieren), dass ich die Frage, ob ich überhaupt Loggen kann (Member ist nullptr) einmalig im LogForwarder klären kann und nicht in jeder Klasse erst den Member fragen muss, ob er Null ist. Ich logge einfach, wenn kein Logger da ist, muss die Frage, ob wirklich geloggt wird, in LogForwarder geklärt. Ich bin also im Gegensatz zu einem Member nicht in Gefahr, mal die Frage zu vergessen und ein SegFault zu provozieren und falls ich doch auf einem SegFault stoße, dann weiß ich, dass das Problem ausschließlich in der Klasse LogForwarder zu suchen ist und ich garantieren kann, dass dieser Bug Problem danach für das komplette Programm erledigt ist.

    Du wolltest ein Log als Member nehmen. Du schreibst

    if( logmember ) logmember->Log( "Bla" );
    

    ich schreibe

    Log( "Bla" );
    

    Beides funktioniert gleichwertig. Bei mir wird die Frage inline dazugepackt, Du musst sie schreiben. Ich kann garantieren, dass ich niemals auf logmember zugreife, wenn es null ist, Du musst sehr diszipliniert sein, um das niemals zu vergessen. Ich bin diszipliniert, mag mich aber nicht aus dem Fenster lehnen, bei so etwas eine Garantie auszusprechen, also typisiere ich eigentlich so ziemlich alles.

    Nehme ich einen Member, bin ich verlockt, einfach ein Log * zu nehmen - es ist einfach weniger Arbeit. Um das Problem über Vererbung zu lösen MUSS ich mein Problem genau zu typisieren.
    Um ebenfalls eine Garantie aussprechen zu können, müsstest auch Du eine Klasse LogForwarder als Member nehmen. Du hast also die gleiche Arbeit, die Forwarderklasse zu schreiben und schreibst dann

    logmember.Log( "Bla" );
    

    Alle Lösungen lösen das gleiche Problem. Nur Du schreibst mehr als ich und wer mehr schreibt, macht mehr Fehler.


Anmelden zum Antworten