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



  • 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.



  • Xin schrieb:

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

    Natürlich. Ich meinte Log*, tut mir leid ich habe eine erschreckend hohe C#-Affinität, die sich gern mal ungewollt zeigt.

    Xin schrieb:

    Nochmal: Dass ich mehr Ableitungen verwende, bedeutet nicht, dass ausschließlich ableite und ich keine Member verwende!

    Auf den Fall bezog sich aber meine Argumentation. Du schriebst ja auf Seite 2, dir schon überlegt zu haben, auf Aggregation komplett zu verzichten. Das hast du ja relativiert.

    Ich logge einfach, wenn kein Logger da ist, muss die Frage, ob wirklich geloggt wird, in LogForwarder geklärt.

    Das ist natürlich ein berechtigter Einwand. In C# (jaja ich weiß...) würde ich das mit Dependency Injection und zusätzlich Contract Based Design lösen: Das Vorhandensein des Logs ist eine Invariante des Typs, respektive eine Vorbedingung der jeweiligen Methode.

    In C++ oder alternativ, je nach Requirement, würde ich den Logger hingegen einmalig im Konstruktor auf nullptr prüfen und ihn dann ggf. auf eine Dummy-Instanz [nenne ich ganz gerne Blackhole Log] setzen, die die Log-Methoden implementiert und schlicht nichts tut. Das geht in Richtung des Null Object Patterns.



  • marco.b schrieb:

    Xin schrieb:

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

    Natürlich. Ich meinte Log*, tut mir leid ich habe eine erschreckend hohe C#-Affinität, die sich gern mal ungewollt zeigt.

    Kein Problem, es war ja abzusehen, was gemeint war.

    marco.b schrieb:

    Xin schrieb:

    Nochmal: Dass ich mehr Ableitungen verwende, bedeutet nicht, dass ausschließlich ableite und ich keine Member verwende!

    Auf den Fall bezog sich aber meine Argumentation. Du schriebst ja auf Seite 2, dir schon überlegt zu haben, auf Aggregation komplett zu verzichten. Das hast du ja relativiert.

    C++ fehlt hier die Möglichkeit, dies auszuformulieren. Darum kann ich in C++ leider nicht auf Member verzichten.

    Wie gesagt, ich arbeite an einer eigenen Sprache. Leider bin ich noch nicht ganz soweit, dass ich ernsthaft vererben kann, aber ich werde da durchaus mehr Experimente machen können als in C++ und syntaktisch meine Art der Programmierung deutlich stärker unterstützen, so dass das ganze hoffentlich auch offensichtlicher ist und sich hoffentlich auch die Konsequenzen herausarbeiten, die in C++-Quelltext nicht vollständig darstellbar sind.

    Ziel ist hierbei durchaus aus Member komplett verzichten zu können.

    marco.b schrieb:

    In C++ würde ich den Logger hingegen einmalig im Konstruktor auf nullptr prüfen und ihn dann ggf. auf eine Dummy-Instanz [nenne ich ganz gerne Blackhole Log] setzen, die die Log-Methoden implementiert und schlicht nichts tut. Das geht in Richtung des Null Object Patterns.

    Ebenfalls eine vom Design her absolut saubere Lösung, die - falls häufiger geloggt als verworfen wird - auch die bessere ist - schließlich würde dann häufig eine überflüssige Frage gestellt. Werden die Logeinträge häufiger verworfen als geloggt, würde ich meine Variante als die schnellere bevorzugen.

    Auch hier sieht man wieder, dass es eben nicht darum geht, eine Lösung als die Perfekte zu verkaufen, es sind zwei Lösungen, die Vor- und Nachteile mit sich bringen und man eben von Fall zu Fall entscheiden muss, was angebrachter ist.



  • Mich würde interessieren, wie genau du vor hast, in so einem System, das auf Member komplett verzichten soll, Zustand zu repräsentieren...



  • dot schrieb:

    Mich würde interessieren, wie genau du vor hast, in so einem System, das auf Member komplett verzichten soll, Zustand zu repräsentieren...

    TypeStates! 😋



  • dot schrieb:

    Mich würde interessieren, wie genau du vor hast, in so einem System, das auf Member komplett verzichten soll, Zustand zu repräsentieren...

    Ich erfinde nicht die Programmierung neu.

    Es sieht nicht nach C++ aus, aber man kann den Aufbau schon ähnlich wie in einer C++-Klasse nachvollziehen.

    Ich würde es eher als Perspektivenwechsel beschreiben oder als Reduktion aufs Wesentliche - inhaltlich ändert sich dabei genauso wenig, wie bei der Frage, ob man ableitet oder einen Member nimmt. Doch je weniger Fragen ich stelle, desto weniger Fragen muss der Entwickler beim Aufbau der Klasse beantworten.
    Entsprechend wird die Definition von Datenstrukturen vergleichsweise sehr kurz.

    Wenn's fertig ist und funktioniert, werde ich es vorstellen, aber das wird noch dauern, schließlich bezahlt mich für die Sprache keiner.



  • Xin schrieb:

    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.

    Lieber Xin,

    der Smily, der Kursiv- und der Unterstrichenen-Text sind nicht umsonst da, vielleicht hätte ich um es besser hervorzuheben etwas durchstreichen sollen, damit die Meldung besser rüber kommt. Ein Begriff und eine Konsequenz wurden markiert, der Kontext, den umgebenden Text, allerdings nicht. Ich weiß echt nicht, welchen übel Gesindel dir über den Weg gelaufen ist: der Anti-Java-Hype-, die Anti-MI-Einstellung *urgh*. Ob du, ich oder jemand anders ein Problem mit dem Diamant-Problem hat, ist bedeutungslos. Von Bedeutung ist die Anwendung der Mehrfachvererbung und die Verantwortung über das Diamant-Problem herr zu werden. Zur Verdeutlichung finde ich daher schon angemessen von ein Problem zu reden.

    Xin schrieb:

    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.

    Häh? Kann nicht an mich gerichtet sein, muss eine Massage an alle sein, oder so.

    Xin schrieb:

    Ein Forum sollte dazu dienen, über diese Schlüsse zu beraten, Hilfestellung oder Inspiration zu geben, sich auszutauschen.

    Du blockst doch aktive. Meine Frage ist noch nicht beantwortet. Aber eigentlich muss du es auch nicht beantworten,..

    Xin schrieb:

    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.

    Häh? Kann nicht an mich gerichtet sein, muss eine Massage an alle sein, oder so.

    Xin schrieb:

    Genausowenig besteht das Recht, erst die Augen zu verdrehen und dazu Wünsche in dieser Form zu äußern.

    Wie bitte oben hattest du mir die Freiheit gegeben und jetzt willst du mein Recht nehmen. Du weiß schon Meinungsfreiheit 😉



  • Xin schrieb:

    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.

    Das Null Object Pattern gibt es schon ewig. Das ist kein bisschen neu oder einzigartig.



  • Zeus schrieb:

    Von Bedeutung ist die Anwendung der Mehrfachvererbung und die Verantwortung über das Diamant-Problem herr zu werden.

    Zur Verdeutlichung finde ich daher schon angemessen von ein Problem zu reden.

    Akzeptiert.
    Allerdings ist dann Programmieren genauso ein Problem, wie auch Editor installieren oder Computer einschalten. Ich habe hier "Problem" als etwas verstanden, dass eine Herausforderung darstellt, nachdem man sich damit beschäftigt hat.

    Den "Smiley" finde ich dennoch sehr unpassend.

    Zeus schrieb:

    Häh? Kann nicht an mich gerichtet sein, muss eine Massage an alle sein, oder so.

    Ist allgemein gehalten, schließt aber keinen aus.

    Zeus schrieb:

    Xin schrieb:

    Ein Forum sollte dazu dienen, über diese Schlüsse zu beraten, Hilfestellung oder Inspiration zu geben, sich auszutauschen.

    Du blockst doch aktive. Meine Frage ist noch nicht beantwortet. Aber eigentlich muss du es auch nicht beantworten,..

    Frage? Die einzige Frage, die Du gestellt hast, war die mit Start und Endpunkt. Ich denke, es ist Dir durchaus selbst möglich von Line auf Strecke schließen zu können, zumal diese Ungenauigkeit überhaupt nichts mit dem dort diskutierten Thema zu tun hat.
    Muss man da wirklich noch drauf eingehen?

    Shade Of Mine schrieb:

    Das Null Object Pattern gibt es schon ewig. Das ist kein bisschen neu oder einzigartig.

    Und? Habe ich derartiges behauptet? Ich habe es nur in gefragten Fall genutzt, weil es sich als sinnvoll aufdrängte, wohingegen die vorgeschlagene offensichtliche Lösung ein Problem provozierte.

    Das Argument ist nicht, ein NOP zu implementieren, sondern genötigt zu werden, ein Problem lösen zu müssen, sich darüber Gedanken machen zu müssen und in diesem speziellen Fall es mit eine NOP abzusichern.

    Damit wäre die Hälfte dieses Postings wieder für Blödsinn draufgegangen. Keiner liest mehr den Anfang, dafür kommen neue Leute rein und wir nehmen die übliche Endlosschleife. Wer ein beliebigen Unterbereich dieses Topics weiter diskutieren möchte, möge ein Passendes im entsprechenden Bereich eröffnen.


Anmelden zum Antworten