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



  • Klar ist das schaffbar.
    Aber plane genug Zeit dafür ein!

    Ich habe mir nach dem Studium, in dem wir hauptsächlich C und Java gelernt haben, intensiv C++ beigebracht. Also 2 gute, dicke Bücher zu C++ und der STL gelesen, und nebenbei ein kleines Hobbyprojekt gestartet, bei dem ich alles gelernte in der Praxis ausprobieren konnte.

    Nach den 2 Monaten war ich noch weit weg vom Ziel, C++ gut zu beherrschen, aber ich konnte es zumindest ganz passabel.
    Ich habe mich danach einfach mal ganz frech auf C++ Stellen beworben. Eben weil diese in meinen Augen mit die interessantesten am Markt sind.
    Bei 2 Gesprächen gabs einen kleinen C++ Test, den ich allerdings immer gut gemeistert habe.
    Nun bin ich schon eine Zeit bei der Firma, lerne jeden Tag neue Dinge von C++, und setze es im Alltag bereits sehr produktiv ein.
    Was ich damit sagen will: Sieh zu dass du die Grundlagen kapierst, und mit der STL vernünftig umgehen kannst.
    Und deine Java Kenntnisse kannst ja auch als Vorteil beim Bewerbungsgespräch anpreisen. Je mehr Sichtweisen man kennt, desto besser.

    Pass aber auf dass du nicht Java mäßigen C++ Code schreibst. Also Null Pointer Exceptions werfen, oder Objekte immer mit new zu erzeugen. Oder alles in Klassen zu verpacken. C++ kennt auch freie Funktionen, so wie C eben, und diese zu verwenden macht durchaus Sinn.

    Gitarre spielen kannst ja dann zur Entspannung 😉



  • TyRoXx schrieb:

    Xin schrieb:

    Du musst Dir eben die üblichen Mängel von Java aneignen: Const-Correctness, Mehrfachvererbung, das Diamant-Problem und virtuelle Vererbung und moderne Programmierung mit Templates.

    Polymorphe Mehrfachvererbung ist ein Mangel von C++. Den hat man bei Java bewusst weggelassen. Also besser nicht benutzen.

    Da gehen die Meinungen auseinander. Ich nutze das sehr gerne, um in meiner Software Redundanz zu vermeiden, ich bin mir aber auch über das Risiko bewusst, in das Diamant-Problem/Feature zu nutzen.
    Darum sollte er es lernen und wenn er es versteht, sollte er es sinnvoll nutzen.
    Ähnlich wie einen Hammer: Wenn man gelernt hat, sich nicht auf den Daumen zu hauen, ist er sehr praktisch, um Nägel in die Wand zu hauen.

    TyRoXx schrieb:

    Zwei wichtige Konzepte in C++ sind RAII und undefiniertes Verhalten.
    Daran erkennt man gut, dass C++ nicht JavaScript/Java ist.

    Undefiniertes Verhalten gibt es bei Java nicht... es provoziert aber meiner Erfahrung stark dazu, es von Hand nach zu implementieren. 😉

    RAII ist allerdings ein sehr wichtiger Punkt, bei dem ich voll auf Deiner Seite bin. Als C++-Entwickler sind die Sachen alle so selbstverständlich. Bei Java merkt man aufeinmal schnell, was man alles nicht mehr hat.



  • Xin schrieb:

    TyRoXx schrieb:

    Xin schrieb:

    Du musst Dir eben die üblichen Mängel von Java aneignen: Const-Correctness, Mehrfachvererbung, das Diamant-Problem und virtuelle Vererbung und moderne Programmierung mit Templates.

    Polymorphe Mehrfachvererbung ist ein Mangel von C++. Den hat man bei Java bewusst weggelassen. Also besser nicht benutzen.

    Da gehen die Meinungen auseinander. Ich nutze das sehr gerne, um in meiner Software Redundanz zu vermeiden, ich bin mir aber auch über das Risiko bewusst, in das Diamant-Problem/Feature zu nutzen.
    Darum sollte er es lernen und wenn er es versteht, sollte er es sinnvoll nutzen.
    Ähnlich wie einen Hammer: Wenn man gelernt hat, sich nicht auf den Daumen zu hauen, ist er sehr praktisch, um Nägel in die Wand zu hauen.

    Vielleicht haust du dir auch nur mit Vererbung auf die Finger und versuchst das jetzt zu verhindern, indem du dir mit Mehrfachvererbung die Arme absägst.

    Das Risiko besteht für mich vor allem darin, dass man Schwachsinn produziert, wenn man alles in Hierarchien zwängt.

    struct Schnabeltier : Ente, Biber
    

    In Java geht das glücklicherweise nicht direkt. In UML-Klassendiagrammen leider schon.
    Aus einer Softwaretechnikvorlesung:

    Person <------ Komponist
      ^                ^
      |                |
      |                |
    Künstler <--- Komponierender Künstler
    

    Das ergibt in Java dann denkbar abenteuerliche Konstruktionen mit zusätzlichen Interfaces und Redundanz.
    Als Alternative hat der Professor immerhin zum Schluss die Möglichkeit vorgeschlagen, Person als Container für abstrakte Rollen zu realisieren.



  • Saxo schrieb:

    Nur ist erlernen von C/C++ wirklich arbeitsintensiv und ohne mehrere Jahre Berufserfahrung darin, wird man ohnehin nicht genommen.

    Es geht schon... Wir haben auch vor fast zwei Jahren einen Uni Absolventen eingestellt, der eher oberflächlich C++ konnte. Auch seine Diplomarbeit darin geschrieben und 2-3 Praktiken damit gemacht, aber da war C++ eben kein Schwerpunkt sondern eher C mit Klassen. Er hat in der Probezeit keinen schönen Code produziert, aber wenn jeder rummeckert und ihm sagt, was ihm nicht gefällt und was man besser machen könnte, dann wird man auch besser. Und jetzt arbeitet er ganz ordentlich mit.
    Der Punkt ist nur, er ist frisch von der Uni gekommen, du hast glaub 20 Jahre Berufserfahrung, mit dir wird man wahrscheinlich nicht so viel Geduld haben und du wirst wahrscheinlich etwas mehr Geld haben wollen. Andererseits kann man sich auch denken, dass jemand mit 20 Jahren Berufserfahrung ein ordentlicher Entwickler ist, der gut mitdenken kann und schon irgendwann in die Sprache reinkommt. Chancen hast du also auf jeden Fall, denk ich.



  • TyRoXx schrieb:

    Xin schrieb:

    Ich nutze das(Mehrfachvererbung) sehr gerne, um in meiner Software Redundanz zu vermeiden, ich bin mir aber auch über das Risiko bewusst, in das Diamant-Problem/Feature zu nutzen.
    Darum sollte er es lernen und wenn er es versteht, sollte er es sinnvoll nutzen.
    Ähnlich wie einen Hammer: Wenn man gelernt hat, sich nicht auf den Daumen zu hauen, ist er sehr praktisch, um Nägel in die Wand zu hauen.

    Vielleicht haust du dir auch nur mit Vererbung auf die Finger und versuchst das jetzt zu verhindern, indem du dir mit Mehrfachvererbung die Arme absägst.

    Vielleicht mache ich das schon das schon ein Jahrzehnt und weiß, was ich tue?

    Meine Arme sind noch dran und auch meinen Fingern sind weich und geschmeidig ohne irgendwelche blutigen Stellen. 🙂

    TyRoXx schrieb:

    Das Risiko besteht für mich vor allem darin, dass man Schwachsinn produziert, wenn man alles in Hierarchien zwängt.

    struct Schnabeltier : Ente, Biber
    

    In Java geht das glücklicherweise nicht direkt.

    Leitet man das Schnabeltier von Ente und Biber ableitet, braucht man keine Mehrfachvererbung um Schwachsinn zu produzieren. 😉
    Das sieht für mich nach einem fehlgeschlagenen Gen-Experiment aus.

    Man kann aber durchaus sinnvolle Dinge ableiten.

    TyRoXx schrieb:

    Als Alternative hat der Professor immerhin zum Schluss die Möglichkeit vorgeschlagen, Person als Container für abstrakte Rollen zu realisieren.

    Vielleicht liest Du Dir Mehrfachvererbung mal an und erzählst mir nicht, was schon meine Profs nicht kapiert haben.



  • aber da war C++ eben kein Schwerpunkt sondern eher C mit Klassen.

    Das hört sich für mich eigentlich ganz vernünftig an. Wenn man etwa die OO-Konstrukte von Go nimmt und sie sinngemäß an C angepasst auf C aufsetzt, kommt für mich was viel stimmigeres raus als es C++ ist. C++ finde ich ein riesen Moloch. Damals wollte man halt auch Anwendungsentwicklung mit C++ machen können und so wollte man was ganz "komfortables" hinstellen. C++ als Toolkit um mit C Go-mäßig zu entwickeln. Das wäre es eigentlich aus meiner Sicht. Müsste ich mal nach einen Weg suchen um das machen zu können :-). Eigentlich finde ich Objective-C einen gelungenen Aufsatz auf C. Leider ist man damit auf Programmierung für iPhone/iPad beschränkt.

    du hast glaub 20 Jahre Berufserfahrung, mit dir wird man wahrscheinlich nicht so viel Geduld haben und du wirst wahrscheinlich etwas mehr Geld haben wollen

    Ja genau, das ist es. Wenn ich keine Verpflichtungen hätte, könnte ich das machen. Es hätte aber bei vielen Leuten zur Folge, dass ich als schlechter Entwickler angesehen werde, ganz einfach weil ich eine Lohneinbuße akzeptiert habe. Vielleicht gibt es einen Weg in einer Stelle so etwa 60-70% Java zu machen und 30-40% C/C++ zu machen.

    Ich habe mir nach dem Studium, in dem wir hauptsächlich C und Java gelernt haben, intensiv C++ beigebracht. Also 2 gute, dicke Bücher zu C++ und der STL gelesen, und nebenbei ein kleines Hobbyprojekt gestartet, bei dem ich alles gelernte in der Praxis ausprobieren konnte.

    Das hört sich gut an. Würde auf jeden Fall Spaß machen.

    Und deine Java Kenntnisse kannst ja auch als Vorteil beim Bewerbungsgespräch anpreisen. Je mehr Sichtweisen man kennt, desto besser.

    Oder für eine Java-Stelle mit etwas C/C++ dabei kann ich eben mein C/C++ anpreisen. Müsste auch gehen.

    Danke für all die Kommentare :-).
    Guet's Nächtle, Saxo



  • Xin schrieb:

    Vielleicht liest Du Dir Mehrfachvererbung mal an und erzählst mir nicht, was schon meine Profs nicht kapiert haben.

    Hmm, du machst das also ungefähr so wie ich befürchtet hatte. Du benutzt zu viel Vererbung. Damit stößt du dann unnötigerweise auf das Diamond-Problem.

    class FullHDTelevisionWithoutPowerSupply 
      : public Display
    {
    public:
      FullHDTelevisionWithoutPowerSupply()
      : Display( 1920, 1080 )
      {}
    };
    
    class FullHDTelevision 
      : public FullHDTelevisionWithoutPowerSupply
      , public PowerConsumer
    {
    public:
      FullHDTelevision( int minWatts, int maxWatts )
       : PowerConsumer( minWatts, maxWatts )
      {}
    };
    
    class BluRayFullHDTelevision 
      : public FullHDTelevision
      , public BluRayPlayer
    {
    public:
      BluRayFullHDTelevision()
        : FullHDTelevision( 3, 250 )
      {
      }
    };
    
    //usw..
    

    Warum benutzt du überhaupt Vererbung? Da sind doch gar keine virtuellen Methoden.
    Ich hätte das ganz einfach mit Aggregation gemacht:

    struct FullHDTelevision
    {
    	FullHDTelevision(Display display, PowerConsumer consumer);
    	const Display &getDisplay() const;
    	const PowerConsumer &getConsumer() const;
    
    private:
    
    	Display display;
    	PowerConsumer consumer;
    };
    
    struct BluRayPlayer
    {
    	explicit BluRayPlayer(PowerConsumer consumer);
    	const PowerConsumer &getConsumer() const;
    	bool isOpen() const;
    
    private:
    
    	PowerConsumer consumer;
    	bool isOpen;
    };
    
    struct BluRayTVCombination
    {
    	BluRayTVCombination(FullHDTelevision tv, BluRayPlayer player);
    	//...
    
    private:
    
    	FullHDTelevision tv;
    	BluRayPlayer player;
    };
    

    Du solltest vielleicht noch einmal dein Verständnis von ist ein überdenken. Oft meint man nämlich in Wirklichkeit eher besteht aus oder kennt.

    Mal ein Beispiel ohne Fernseher:

    struct Burger
    {
    	std::vector<BurgerLayer> layers;
    };
    
    struct PommesFrites
    {
    	bool large;
    	bool withMajo;
    };
    
    struct Drink
    {
    	enum class Type
    	{
    		Coke,
    		Sprite,
    		Water
    	};
    
    	Type type;
    	bool large;
    };
    
    struct MenuA
    {
    	Burger burger;
    	PommesFrites pommes;
    	Drink drink;
    };
    
    struct MenuB : Burger, PommesFrites, Drink
    {
    };
    

    Würdest du MenuA oder MenuB implementieren?



  • TyRoXx schrieb:

    Xin schrieb:

    Vielleicht liest Du Dir Mehrfachvererbung mal an und erzählst mir nicht, was schon meine Profs nicht kapiert haben.

    Hmm, du machst das also ungefähr so wie ich befürchtet hatte. Du benutzt zu viel Vererbung. Damit stößt du dann unnötigerweise auf das Diamond-Problem.

    Schau es Dir den Link nochmal an... Da ist kein Diamant. Du hast das Falsche kopiert.
    Und was Du unter "zu viel" verstehst ist subjektiv.

    TyRoXx schrieb:

    Warum benutzt du überhaupt Vererbung? Da sind doch gar keine virtuellen Methoden.

    Weil ich nicht nachimplementieren und nichts nicht weiterleiten muss und das Gerät sofort fertig beschrieben habe. Wer weniger programmiert macht auch weniger Fehler.

    TyRoXx schrieb:

    Ich hätte das ganz einfach mit Aggregation gemacht:

    Und schöne überflüssige getBla() Funktionen implementiert.

    TyRoXx schrieb:

    Du solltest vielleicht noch einmal dein Verständnis von ist ein überdenken. Oft meint man nämlich in Wirklichkeit eher besteht aus oder kennt.

    Ich denke, mein Verständnis von "ist ein" passt schon. Ein Fernseher mit eingebauten BluRay-Player ist ein Fernseher.

    TyRoXx schrieb:

    Mal ein Beispiel ohne Fernseher: ...

    Würdest du MenuA oder MenuB implementieren?

    Ich würde keins von beiden implementieren, ich implementiere keine Lebensmittel.

    Semantisch besteht ein Menü aus den Komponenten. Also würde ich MenuA bevorzugen. Ich habe mit MenuB aber kein Problem. Erzählst Du Deinen Freunden, dass Du den Burger aus dem Sparmenü und die Fritten aus dem Sparmenü gegessen hast und dann die Cola aus dem Sparmenü getrunken hast oder hast Du Dir einfach das Sparmenü reingezogen?

    Ich würde das also davon abhängig machen, ob ich die Komponenten einzeln betrachten möchte oder ein neues Konstrukt haben möchte. Fernseher mit Blurayplayer => Neues Konstrukt in einem Gehäuse und nicht eine Kiste, wo ich zwei Geräte reinlege.
    Ich würde also wieder MenüA bevorzugen, weil das Menü weiterhin aus drei getrennten Komponenten in einer Tüte besteht und nicht komplett in den Mixer gegeben wurden und damit ein Konstrukt entstanden ist.

    Die Mehrfachvererbung erlaubt mir diesen semantischen Unterscheid zu dokumentieren und auch anzuwenden: Ich kann einen Film auf einem Display sehen, auf einem Fernseher oder einem BluRay-TV-Kombigerät. Wenn Du großen Wert darauf legst zu betonen, dass Du den Film nicht auf dem Rahmen vom Fernseher siehst, dann solltest Du tatsächlich auf die Ableitungen verzichten und jede Menge Getter() implementieren.

    Was Du als "zuviel" bezeichnest ist subjektiv. Ich entwickle praktische Klassen, die Probleme lösen. Ich gucke Filme auf BluRay-FullHD-Kombi-Gerätes - fertig. Du schaust dann halt auf dem Display, dass Du per Getter aus dem FullHDFernsehers geholt hast, den Du per Getter aus dem BluRay-FullHD-Kombi-Gerätes geholt hast. Interessiert mich das?
    Diese Betonung muss einen Wert haben. Hat sie keinen Wert, ist es Boilerplate, das bedeutet Mehraufwand beim Schreiben und beim Lesen des Quelltextes und zusätzlich bedeutet mehr Quelltext auch mehr Chancen, Fehler einzubauen.

    Vielleicht benutzt Du auch einfach zu wenig Mehrfachvererbung. Alles subjektiv.



  • In meinen Programmen kommt nur sehr selten Vererbung vor, Mehrfachvererbung habe ich noch nie benutzt. Ein gutes Beispiel waere angebracht.



  • knivil schrieb:

    In meinen Programmen kommt nur sehr selten Vererbung vor, Mehrfachvererbung habe ich noch nie benutzt. Ein gutes Beispiel waere angebracht.

    Was ein gutes Beispiel ist, ist letztendlich wieder willkürlich. Man kann alles mit Aggregation machen. Wem das besser gefällt... Mehrfachvererbung hilft mir deutlich dabei, Typen schärfer zu differenzieren und ich spielte durchaus schon mit dem Gedanken, Aggregationen komplett abzuschaffen: weniger Boilerplate und zwangsläufig scharfe Typdefinitionen. Das will man doch!?

    Wer "ist ein" und "besteht aus" heilig spricht, darf das gerne tun, möge aber nicht damit argumentieren, dass sein Professor das gesagt hat, sondern die Gründe im Einzelfall auch benennen können. Wenn ich was aus dem Studium gelernt habe, dann dass man Professoren nicht dogmatisch folgen sollte, sondern sie als "Inspiration" verstehen sollte, einen Weg nach vorne zu finden.

    Ich bevorzuge bei manchen Dingen Funktionalität schnell neu zusammenführen zu können und fertig.
    Sehr häufig passiert das, wenn ich ein Objekt mit unterschiedlichen Konzepten ausstatten möchte. Nehmen wir an, ich möchte ein Bildbearbeitungsprogramm schreiben:

    class BitmapDocument : public Node < BitmapDocument >
                           , public Filename
                           , Bitmap
    {};
    

    Kann man alles aggregativ machen, aber ein Bitmapdokument ist auch eine Node, eine Repräsentation einer Datei und eine Bitmap. Eben wie ein technischer Bauplan, auf dem unten rechts diverse Angaben aufgedruckt sind und der Rest des Papiers zum Zeichnen da ist und an der Seite zwei Löcher, um ihn abzuheften. Das ist ein neues Konstrukt und nicht mehr nur eine Zeichnung, trotzdem kann man auf dem Bauplan zeichnen.
    Ich baue mir noch einen Konstruktor dazu und bin fertig.

    Sehr schön, wenn man Sachen ändert - ich muss weniger Getter ändern, keine Member.

    Ich nutze durchaus auch Mehrfachvererbung, um mehrfach von Node<> abzuleiten, wenn ich Objekte in mehreren Listen verwalten möchte - so könnte Bitmap ebenfalls von Node< Bitmap > abgeleitet sein, falls ich davon eine eigene Liste pflegen würde - eben eine List<Bitmap>, die sich ganz eindeutig gegenüber einer List<BitmapDocument> unterscheidet.

    Ich habe kein Problem damit, wenn Leute anders programmieren, weil man es ihnen anders beigebracht hat. Ich beschäftige mich seit über 10 Jahren mit Sprachdesign und habe inzwischen eine eigene Schlachtbank für heilige Kühe zu Hause.
    Mir hat man auch viel beibebracht, ich besitze die Scott Meyers Bücher beide und habe sie auch gelesen. Es sind hervorragende Bücher. Aber wir stellen heute auch die Bibel in Frage und genauso stelle ich ein "ist ein"-Dogma in Frage. Für mich stellt sich nicht die Frage, ob Scott Meyers oder ein anderer Prof das gesagt hat, sondern nur "Hat das ganze für mich einen praktischen Nutzen?"
    Und die Strategien, die Java/C# hier verfolgen, halte ich für ausgemachten Blödsinn.

    Ich bin heute absolut überzeugt, dass die Mehrfachvererbung wieder kommt. Dafür steckt viel zuviel Potential darin, Boilerplate zu sparen und den Entwickler dazu zu nötigen, sich eindeutig auszudrücken. Ein vollkommen unterschätztes Konzept.



  • Xin schrieb:

    Ich bin heute absolut überzeugt, dass die Mehrfachvererbung wieder kommt. Dafür steckt viel zuviel Potential darin, Boilerplate zu sparen und den Entwickler dazu zu nötigen, sich eindeutig auszudrücken. Ein vollkommen unterschätztes Konzept.

    🤡 🤡 🤡



  • Ich bin heute absolut überzeugt, dass die Mehrfachvererbung wieder kommt. Dafür steckt viel zuviel Potential darin, Boilerplate zu sparen und den Entwickler dazu zu nötigen, sich eindeutig auszudrücken. Ein vollkommen unterschätztes Konzept.

    Aber sicher nicht wie C++ das macht.
    Sondern eher mit traits ala Scala.



  • 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.
    Außerdem kannst du Kapselung schwieriger steuern, bei Aggregation mit Komponente K kannst du die Implementierung für Aggregationspartner leicht verstecken und dabei für Subklassen von K immernoch gewisse Teile des Zustands zugreifbar machen [protected].



  • 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? Wenn ich beschreibe, dass ein Kleiderbügel ein Ausführung eines Hakens mit Kleiderhalter ist und eine Ausführung von Kleiderhalter ist - oder ob ein Kleiderbügel aus einen Haken und einem Kleiderhalter besteht, so habe ich so oder so einen Kleiderbügel.

    Das hat keinen Einfluss darauf, wie die der Haken mit der Kleiderstange kommuniziert oder der Kleiderhalter mit der Kleidung!?

    Oder verstehe ich Deinen Punkt hier gerade nicht korrekt?

    marco.b schrieb:

    Außerdem kannst du Kapselung schwieriger steuern, bei Aggregation mit Komponente K kannst du die Implementierung für Aggregationspartner leicht verstecken und dabei für Subklassen von K immernoch gewisse Teile des Zustands zugreifbar machen [protected].

    Wie gesagt, ich habe auch kein Problem mit Aggregationen, ich nutze die Mehrfachvererbung nur gerne, als öffentliche Aggregation mit vereinfachtem Zugriff. Vererbung ist nichts anderes als Aggregation.
    Man schreibt seine Klassen entsprechend so, dass man gut von ihnen ableiten kann und arbeitet wie mit Lego.
    Finde ich sehr praktisch, spart Code und das ist in meinen Augen auch semantisch zu vertreten, denn "besteht aus" und "ist ein" ist häufig nur die Frage, wie man sich der Sache nähert.

    Die Farbe eines Autos, ist ein öffentliches Attribut. Mein Auto ist keine Farbe. Trotzdem ist mein Auto blau (myCar == Color::Blue;). Ich kann auch sagen, die Farbe von meinem Auto ist blau (myCar.Color == Color::Blue;). Die Frage ist schlussendlich, wieviel Kontext brauchst Du?
    Bei einer Funktion Color::Mix( yourCar, myCar ) muss man selbst wissen, ob man erwähnen muss, dass man die Farbe meint? Wenn das undurchschaubar ist, geht das über Aggregation natürlich deutlicher: Color::Mix( yourCar.Color, myCar.Color )

    Dein Punkt ist absolut korrekt, wenn es darum geht, Daten zu schützen, zum Beispiel das FileHandle in einer class File.

    Allerdings interessieren sich Funktionen, die z.B. nur mit einem PowerSupply arbeiten nicht dafür, was das reingereichte Objekt denn noch so alles kann. Ich sehe nicht, was ich hier verstecken müsste, vielleicht ein BluRay-Player, vielleicht ein Fernseher, egal... ich frage nur, wieviel Strom das Ding verbraucht: ganz öffentliche Funktion.

    Ich halte Mehrfachvererbung für ein sehr praktisches Mittel, Quellcodes kurz zu halten und mache reichlich Gebrauch davon und kann das Dogma, dass Mehrfachvererbung Werk des Teufels wäre, absolut nicht nachvollziehen.



  • Xin schrieb:

    Ich gucke Filme auf BluRay-FullHD-Kombi-Gerätes - fertig. Du schaust dann halt auf dem Display, dass Du per Getter aus dem FullHDFernsehers geholt hast, den Du per Getter aus dem BluRay-FullHD-Kombi-Gerätes geholt hast. Interessiert mich das?

    Ich finde die ganze Klassenhierachie eh unglücklich, aber um mal bei dem Beispiel zu bleiben:
    Wie implementierst du denn allgemeine Funktionen, die auf der Basisklasse arbeiten? Sowas wie

    bool canPlayBlueRay(Display* display)
    

    Mit dynamic_cast?



  • Jockelx schrieb:

    Xin schrieb:

    Ich gucke Filme auf BluRay-FullHD-Kombi-Gerätes - fertig. Du schaust dann halt auf dem Display, dass Du per Getter aus dem FullHDFernsehers geholt hast, den Du per Getter aus dem BluRay-FullHD-Kombi-Gerätes geholt hast. Interessiert mich das?

    Ich finde die ganze Klassenhierachie eh unglücklich, aber um mal bei dem Beispiel zu bleiben:
    Wie implementierst du denn allgemeine Funktionen, die auf der Basisklasse arbeiten? Sowas wie

    bool canPlayBlueRay(Display* display)
    

    Mit dynamic_cast?

    Gar nicht. Die Frage hat auch nix mit Mehrfachvererbung zu tun.

    Ein Display kann keine Blurays abspielen. Darum ist es ein Display. Das Display selbst hat auch keine Verwandtschaft mit einem BluRayPlayer. Die Antwort wäre also grundsätzlich 'false'. Wenn ich die Antwort kenne, ist die Frage überflüssig.

    Eine Funktion, die auf der Basisklasse arbeitet und sich für BluRay-Funktionalität interessiert würde als Parameter einen BluRayPlayer erwarten und der würde "bool canPlayBlueRay(BluRayPlayer * player)" grundsätzlich true antworten und - hatten wir ja schon: Wenn ich die Antwort kenne, ist die Frage überflüssig.

    Was immer ich mit canPlayBlueRay() reinstecken kann - sagen wir mal this - ist von irgendeinem Typen, der mindestens von Display geerbt hat.

    Deine Frage wäre mit dynamic_cast zu beantworten, aber nicht schön zu lesen.
    Wenn ich diese Frage also stellen möchte, dann muss ich mich (also this) fragen, ob ich BluRays abspielen kann.

    Beispiel aus meiner Praxis: Ich programmiere eine eigene Programmiersprache, dort gibt es Operatoren (z.B. Addition oder Negierung). Hier stellt sich zum Beispiel unter Umständen die Frage, ob das ein binärer Operator ist.

    Die Vererbungs Hierarchie ist dann Operator->PriorityOperator->BinaryOperator->Addition
    Operator definiert die Frage virtual bool IsBinary() const { return false; }, BinaryOperator überschreibt die Funktion mit { return true; }.

    Wenn Du das nicht von Hand ändern möchtest, dann wäre der dynamic_cast das Mittel der Wahl - würde aber zur Laufzeit mehr Zeit in Anspruch nehmen.

    bool IsBinary() const { return nullptr != dynamic< BinaryOperator const * >( this ); }
    

    Aber wie gesagt - das ist eine gerade Vererbungslinie: Das hat nichts mit Displays zu tun oder Mehrfachvererbung zu tun.
    Du müsstest Du dem Display die Fragemöglichkeit mitgeben, ob es BluRays abspielen kann und diese Information - vollkommen unabhängig von der Mehrfachvererbung an der Stelle ändern, wo Du per Mehrfachvererbung den BlurayPlayer dazupackst.
    In dem Fall musst Du ohne Mehrfachvererbung sogar auf die return true/false-Lösung ausweichen, da Du zur Laufzeit nicht mehr prüfen kannst, ob Deine Instanz eine Aggregation BluRayPlayer besitzt und selbst dann wäre das übergebene Display nur das Display und Du hättest mit dem (Display *)-Datentyp keine Chance mehr an den BluRay-Player ran zu kommen, der eventuell in dem Objekt sitzt, in dem das Display als Member enthalten ist.

    Entsprechend wirst Du Deine Funktionen so formulieren, dass Du diese Probleme gar nicht hast, womit wir wieder bei der ersten Antwort auf Deine Frage sind: Eine Frage wie "bool canPlayBlueRay(Display* display)" muss ich nicht implementieren und wenn doch habe ich etwas verkehrt gemacht, was nichts mit Mehrfachvererbung zu tun.

    Hier holst Du Dir über dynamic_cast nur die Möglichkeit rein, eine Frage, zu beantworten, die Du Dir hoffentlich gar nicht stellen musst.



  • Was genau ist der Sinn davon, ein Document von Filename erben zu lassen!?

    Xin schrieb:

    Beispiel aus meiner Praxis: Ich programmiere eine eigene Programmiersprache, dort gibt es Operatoren (z.B. Addition oder Negierung). Hier stellt sich zum Beispiel unter Umständen die Frage, ob das ein binärer Operator ist.

    Was hat die Basisklasse Operator für einen Sinn, wenn du später dann solche Fragen stellst?

    Xin schrieb:

    Vererbung ist nichts anderes als Aggregation.

    Wieso kann ich ein Objekt von abgeleitetem Typ dann wie ein Objekt vom Typ der Basisklasse behandeln? Das widerspricht doch dem Grundgedanken von Aggregation!?



  • dot schrieb:

    Was genau ist der Sinn davon, ein Dokument von Filename erben zu lassen!?

    Das ist so nie geschrieben worden.

    BitmapDocument erbte von Document, Filename und Bitmap.

    Es ist auch nur ein Beispiel. Ich habe zwar die jeweiligen Klassen, aber afair keine Klasse BitmapDocument. Was ich damit fabriziert habe, hätte einen eigenen Artikel zur Erklärung benötigt, also habe ich das nur flott auf BitmapDocument runtergebrochen.

    PS: Und der Rest:

    dot schrieb:

    Xin schrieb:

    Beispiel aus meiner Praxis: Ich programmiere eine eigene Programmiersprache, dort gibt es Operatoren (z.B. Addition oder Negierung). Hier stellt sich zum Beispiel unter Umständen die Frage, ob das ein binärer Operator ist.

    Was hat die Basisklasse Operator für einen Sinn, wenn du später dann solche Fragen stellst?

    Funktionalität hochziehen, die nicht in den einzelnen Operatoren ausgeführt werden muss. Die Frage stellt sich, wenn ein Algorithmus in class Operator für eine bestimmte Sorte von Operatoren anders funktioniert, wenn es ich um einen Binären Operator handelt.

    dot schrieb:

    Xin schrieb:

    Vererbung ist nichts anderes als Aggregation.

    Wieso kann ich ein Objekt von abgeleitetem Typ dann wie ein Objekt vom Typ der Basisklasse behandeln? Das widerspricht doch dem Grundgedanken von Aggregation!?

    Das hängt sehr stark davon ab, was Du unter Aggregation verstehst.

    Aggregation ist eine "Haufen von Zeugs" und sagt nix darüber wer hier welche Rechte hat.

    Bei:

    struct A
    {
      int a;
    };
    
    struct B : public A
    {
      int b;
    } bi;
    
    struct C
    {
      A a;
      int b;
    } ci;
    

    unterscheiden sich class B und class C überhaupt nicht. Du darfst die Instanz bi überall da reinwerfen, wo Du A reinwerfen darfst und bei C musst Du halt ci.a schreiben.

    Am Programm ändert das nichts - es stellt sich nur die Frage, ob Du das betonen möchtest oder nicht.

    Eine Aggregation ist beides: die Anhäufung der beiden Membern a und b, jeweils 4 Byte groß. Du kannst lustig hin- und hercasten. Knorr oder Maggie - alles Tütensuppe.



  • Xin schrieb:

    dot schrieb:

    Was genau ist der Sinn davon, ein Dokument von Filename erben zu lassen!?

    Das ist so nie geschrieben worden.

    BitmapDocument erbte von Document, Filename und Bitmap.

    Erklär mir bitte, wie das sein kann, dass ein BitmapDocument, das von Document, Filename und Bitmap erbt, nicht von Filename erbt!?

    Xin schrieb:

    dot schrieb:

    Xin schrieb:

    Beispiel aus meiner Praxis: Ich programmiere eine eigene Programmiersprache, dort gibt es Operatoren (z.B. Addition oder Negierung). Hier stellt sich zum Beispiel unter Umständen die Frage, ob das ein binärer Operator ist.

    Was hat die Basisklasse Operator für einen Sinn, wenn du später dann solche Fragen stellst?

    Funktionalität hochziehen, die nicht in den einzelnen Operatoren ausgeführt werden muss. Die Frage stellt sich, wenn ein Algorithmus in class Operator für eine bestimmte Sorte von Operatoren anders funktioniert, wenn es ich um einen Binären Operator handelt.

    Wenn ein Algorithmus für eine bestimmte Sorte von Operatoren anders funktioniert, dann bedeutet das, dass er nicht für alle Operatoren gleich funktioniert. Damit stellt sich unweigerlich die Frage, wieso das Interface des Algorithmus das Gegenteil behauptet, indem es jeden beliebigen Operator entgegen nimmt. (Siehe auch: http://en.wikipedia.org/wiki/Liskov_substitution_principle)

    Xin schrieb:

    Bei:

    [...]

    unterscheiden sich class B und class C überhaupt nicht. Du darfst die Instanz bi überall da reinwerfen, wo Du A reinwerfen darfst und bei C musst Du halt ci.a schreiben.

    Das ist nicht das gleiche und funktioniert überhaupt nur, weil C::a public ist.

    Wenn du tatsächlich der Meinung bist, dass Vererbung und Aggregation äquivalent sind, dann erklär mir, basierend auf dieser Annahme, bitte den Output von folgendem Programm:

    #include <iostream>
    
    struct A 
    { 
    }; 
    
    struct B : public A 
    { 
      int b; 
    }; 
    
    struct C 
    { 
      A a; 
      int b; 
    };
    
    int main()
    {
      std::cout << sizeof(A) << '\n' << sizeof(B) << '\n' << sizeof(C) << '\n';
    }
    

    Output (MSVC 11 x64):

    1
    4
    8
    


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


Anmelden zum Antworten