Ist OOP in C++ eigentlich noch guter Stil?



  • out schrieb:

    Was ist statische Polymorphie?

    Bloss nicht googeln, sonst findest du es am ende noch raus.
    http://en.wikipedia.org/wiki/Template_metaprogramming#Static_polymorphism


  • Mod

    out schrieb:

    Was ist statische Polymorphie?

    Na, das was der TE hier wohl vor hat. In C++ in der Regel mittels CRTP umgesetzt:
    template <class Derived>

    Wikipedia schrieb:

    template <typename Derived> struct base
    {
        void interface()
        {
             // ...
             static_cast<Derived*>(this)->implementation();
             // ...
        }
    };
     
    struct derived : base<derived>
    {
         void implementation();
    };
    


  • CRTP hätte allerdings den Nachteil, dass ich für alle Figuren eine Funktion implementieren muss und nicht auf die Standardfunktion zurückgreifen kann.

    Soweit ich weiß ich mit CRTP auch keine Polymorphie bei tieferer Vererbung mehr möglich.

    Beispiel:

    struct Spielfigur {
     virtual void bewege();
    };
    
    struct Buerger : Spielfigur {
     void bewege() override;
    };
    
    struct Reservist : Buerger {
     void bewege() override;
    };
    

    Wüsste ich nicht, wie ich das mit CRTP umsetzen sollte.


  • Mod

    Kolonist schrieb:

    CRTP hätte allerdings den Nachteil, dass ich für alle Figuren eine Funktion implementieren muss und nicht auf die Standardfunktion zurückgreifen kann.

    Soweit ich weiß ich mit CRTP auch keine Polymorphie bei tieferer Vererbung mehr möglich.

    Beispiel:

    struct Spielfigur {
     virtual void bewege();
    };
    
    struct Buerger : Spielfigur {
     void bewege() override;
    };
    
    struct Reservist : Buerger {
     void bewege() override;
    };
    

    Wüsste ich nicht, wie ich das mit CRTP umsetzen sollte.

    😕 Das ist doch quasi eine Auflistung der Fälle, für die der Trick überhaupt erfunden wurde.

    #include <iostream>
    
    using namespace std;
    
    template <typename Derived> struct base
    {
      void interface()
      {
        static_cast<Derived*>(this)->implementation();
      }
      void implementation()
      {
        cout << "Default\n";
      }
    };
    
    struct overrider : base<overrider>
    {
      void implementation()
      {
        cout << "Overridden\n";
      }
    };
    
    struct nonoverrider : base<nonoverrider>
    {
    };
    
    template <typename twicederived> struct derived : base<twicederived> 
    {
      void implementation()
      {
        cout << "Derived Base\n";
      }
    };
    
    struct twicederivednonoverrider : derived<twicederivednonoverrider>
    {
    };
    
    struct twicederivedoverrider : derived<twicederivedoverrider>
    {
      void implementation()
      {
        cout << "Derived overridden\n";
      }
    };
    
    int main()
    {
      overrider overrider;
      nonoverrider nonoverrider;
      twicederivednonoverrider twicederivednonoverrider;
      twicederivedoverrider twicederivedoverrider;
    
      overrider.interface();
      nonoverrider.interface();
      twicederivednonoverrider.interface();
      twicederivedoverrider.interface();
    }
    

    https://ideone.com/YDFXmp

    P.S.: Das bedeutet aber nicht, dass ich das in diesem Fall empfehlen würde. Ich habe nur festgestellt, dass es wohl das ist, was du im Eingangsbeitrag suchst. Die wirkliche Empfehlung ist aber

    Eisflamme schrieb:

    Verschiedene Container für verschiedene Figurentypen? Super skalierbar. Sobald Du einen neuen Figurentyp hinzufügst, musst Du den neuen Container anlegen und bei allen Schleifen, die über die Container laufen, noch eine weitere für den neuen Container anlegen.

    virtual function overhead < mehrere container overhead

    P.P.S: Obige Ungleichung hängt natürlich vom Anwendungsfall ab, aber erfahrungsgemäß braucht das Verhältnis Klassen:Instanzen nicht groß zu sein, damit die Aussage stimmt.


  • Mod

    Kolonists Problem besteht vermutlich darin, dass er mit
    Spielfigur -> Buerger -> Reservist
    beabsichtigt, sowohl Buerger als auch Reservist als nichtabstrakte konkrete Klassen einzusetzen. Und das ist dann mit CRTP etwas schwieriger. Andererseits würde so eine Benutzung sowieso das LSP verletzen...



  • Ich denke, man kommt um virtuelle Funktionen gar nicht herum, wenn man nicht irgendwelche anderen Tricks einsetzen will, die aber auch keine/kaum Geschwindigkeitsvorteile bringen.
    Man weiß ja beim Compilen noch gar nicht, welchen Einheitentyp man vor sich hat.
    Es kann ja sein, dass der Spieler einen normalen Bürger, einen Soldaten oder einen Panzer baut. Da das dynamisch ist, müsste man das mit switch-case abfragen und da hat doch wohl eine virtuelle Funktion klare Vorteile, sowohl von der Übersicht als auch möglicherweise von der Geschwindigkeit.


  • Mod

    Marthog schrieb:

    Ich denke, man kommt um virtuelle Funktionen gar nicht herum, wenn man nicht irgendwelche anderen Tricks einsetzen will, die aber auch keine/kaum Geschwindigkeitsvorteile bringen.
    Man weiß ja beim Compilen noch gar nicht, welchen Einheitentyp man vor sich hat.
    Es kann ja sein, dass der Spieler einen normalen Bürger, einen Soldaten oder einen Panzer baut. Da das dynamisch ist, müsste man das mit switch-case abfragen und da hat doch wohl eine virtuelle Funktion klare Vorteile, sowohl von der Übersicht als auch möglicherweise von der Geschwindigkeit.

    Er hat etwas vor in der Art von

    vector<Bürger> bürger;
    vector<Soldat> soldaten;
    // usw. für alle Einheitentypen
    
    // Etwas, das mit allen Einheiten gemacht werden soll:
    for (auto einheit: bürger) einheit.tu_was();
    for (auto einheit: soldat) einheit.tu_was();
    // usw. für alle Einheitentypen
    

    Wobei die genaue Auswahl der tu_was-Funktion von der Vererbungshierarchie abhängen soll, insbesondere mit der Möglichkeit, Implementierungen zu erben oder zu überschreiben.



  • for (auto einheit: bürger) einheit.tu_was();
    

    Ich hoffe, dass diese Zeile pseudocode-mäßig gemeint ist ...



  • sind umlaute in c++ nicht ein absolutes no-go?



  • LOL! schrieb:

    sind umlaute in c++ nicht ein absolutes no-go?

    Das ist nicht das Problem.
    Sondern die Schleife an sich, das wäre im Bereich typischer Anfänger Fehler.


  • Mod

    Meine Güte! Seid ihr wirklich alle nicht in der Lage, Pseudocode zu erkennen, wenn er euch ins Gesicht springt? Hätte ich

    Schleife über alle Bürger
    BEGIN
      tu was
    END
    

    schreiben müssen?



  • Wenn man fuer jede Einheit einen extra Container hat, dann wird es sehr umstaendlich, neue Einheiten hinzuzufuegen.



  • @SeppJ
    Wenn man was schreibt was gültiger C++ Code ist (oder fast, abgesehen von Trivialitäten), dann wäre net schlecht wenn er auch Sinn macht/funktioniert. Weil er dann vermutlich schnell als konkretes Beispiel verstanden wird.
    Und dann fehlt da nach dem auto ein & .
    Weil sonst immer nur eine Kopie des Bürgers & Soldaten modifiziert wird, was ja nicht Sinn der Sache ist.

    @Sone
    Statt nur aufzeigen wo was falsch ist/fehlt/... könntest du vielleicht auch gleich dazuschreiben was. Das wäre sinnvoll. Im Gegensatz zu nur rummoppeln dass irgendwas nicht passt, ohne zu sagen was. Was dann halt nicht so sinnvoll ist.


  • Mod

    hustbaer schrieb:

    @SeppJ
    Wenn man was schreibt was gültiger C++ Code ist (oder fast, abgesehen von Trivialitäten), dann wäre net schlecht wenn er auch Sinn macht/funktioniert. Weil er dann vermutlich schnell als konkretes Beispiel verstanden wird.
    Und dann fehlt da nach dem auto ein & .
    Weil sonst immer nur eine Kopie des Bürgers & Soldaten modifiziert wird, was ja nicht Sinn der Sache ist.

    Und das ändert inwiefern etwas am Sinn des Beispiels? Ist doch vollkommen egal, ob das Original oder eine Kopie angefasst wird, weil überhaupt nicht klar ist, was überhaupt gemacht wird. Eine Kopie kann genau so richtig sein, je nach Aufgabenstellung. Punkt ist: Es wird über alle Objekte iteriert. Das ist wichtig. Das wird mit möglichst wenig Syntaxballast vorgeführt.

    Wer da krampfhaft ein '&' vermisst, der weiß sowieso dass da eines hin sollte/könnte. Der soll sich dann bitte auch beschweren, dass der Code nicht im Blockscope ist, dass Umlaute nicht erlaubt sind, #include<vector> fehlt und keine einzige der Klassen und Funktionen deklariert ist. Wenn ihm dann noch nicht aufgefallen ist, was der Unterschied zwischen echtem Code und Vorführbeispielen ist, dann ... ach keine Ahnung. Dann ist er eben einfach zu dumm. Das sind diese Schnösel, wegen denen man hier keinerlei Antworten geben kann, ohne 80% des Textes damit zu verschwenden, alle Sonderfälle aufzuzählen, in denen eine gemachte Aussage (also die eigentliche Antwort auf eine Frage) vielleicht doch nur bedingt gelten könnte.



  • Statt nur aufzeigen wo was falsch ist/fehlt/... könntest du vielleicht auch gleich dazuschreiben was. Das wäre sinnvoll. Im Gegensatz zu nur rummoppeln dass irgendwas nicht passt, ohne zu sagen was. Was dann halt nicht so sinnvoll ist.

    Und du, meine holder hustbaer in der Ferne, hast gleich, um den Kontrast aufzuzeigen, eine volle Erklärung abgeliefert*, damit selbst SeppJ, der bekanntlich etwas langsam ist</ironie>, kapiert, wo sein halb-pseudocode-halb-kompilierbarer-code-Mischmasch eine Macke hat. 👍

    ~*Du bist einfach ein so viel besserer Mensch als ich, dass ich jeden Tag aufs neue nicht fassen kann, wie du es schaffst die Energie aufzubringen mir noch zu erklären was ich (so tollpatschig und faul ich nun einmal bin), "[..] mal wieder, leider", versaut habe.~

    Edit: Ohne hier etwas ausarten zu lassen, entschuldige ich mich für meine völlig inadäquate Bemerkung, das war wirklich überflüssig, es tut mir Leid. Bitte nicht meinetwegen Off-Topic geraten, war meine Schuld. 👎 (Lösche die Diskussion ab meinem Post einfach weg)



  • SeppJ schrieb:

    hustbaer schrieb:

    @SeppJ
    Wenn man was schreibt was gültiger C++ Code ist (oder fast, abgesehen von Trivialitäten), dann wäre net schlecht wenn er auch Sinn macht/funktioniert. Weil er dann vermutlich schnell als konkretes Beispiel verstanden wird.
    Und dann fehlt da nach dem auto ein & .
    Weil sonst immer nur eine Kopie des Bürgers & Soldaten modifiziert wird, was ja nicht Sinn der Sache ist.

    Und das ändert inwiefern etwas am Sinn des Beispiels? Ist doch vollkommen egal, ob das Original oder eine Kopie angefasst wird, weil überhaupt nicht klar ist, was überhaupt gemacht wird. Eine Kopie kann genau so richtig sein, je nach Aufgabenstellung. Punkt ist: Es wird über alle Objekte iteriert. Das ist wichtig. Das wird mit möglichst wenig Syntaxballast vorgeführt.

    Wer da krampfhaft ein '&' vermisst, der weiß sowieso dass da eines hin sollte/könnte.

    Es geht doch genau um die Leute die das nicht wissen bzw. nicht auf den ersten Blick sehen. Und es dann so abschreiben. Und sich dann wundern dass es nicht tut was es sollte.

    Der soll sich dann bitte auch beschweren, dass der Code nicht im Blockscope ist, dass Umlaute nicht erlaubt sind, #include<vector> fehlt und keine einzige der Klassen und Funktionen deklariert ist.

    Bis auf die Umlaute sind das alles übliche Dinge bei Beispiel-Code. Aber Beispiel-Code != Pseude-Code.

    Wenn ihm dann noch nicht aufgefallen ist, was der Unterschied zwischen echtem Code und Vorführbeispielen ist, dann ... ach keine Ahnung.

    Ehm.
    Anfänger?

    Ich weiss auch nicht warum du dich so aufregst. Klar, es geht hier um "nix". Aber trotzdem werd ich wohl noch meine Meinung schreiben dürfen.



  • @Sone
    Dir hat's grad komplett die Kette ausgehängt, oder?

    Erklär mir mal: was bitte bringt es zu schreiben "das ist falsch", wenn man damit nicht entweder
    a) Etwas korrigieren/richtigstellen will
    oder
    b) Jemandem ans Bein pinkeln will
    oder
    c) Sich blöd wichtig machen will
    ?

    Falls dein Beweggrund (a) gewesen sein sollte, dann fehlt für mich schon irgendwie die Info was eigentlich das Problem ist. Ich meine für wen schreibt man sowas - für Leute die es selbst schon wissen? Eher nicht...

    Und falls doch eher (b) oder (c), dann halt gefälligst einfach die Fresse.
    Bzw. halt überhaupt die Fresse, du nervst.



  • knivil schrieb:

    Wenn man fuer jede Einheit einen extra Container hat, dann wird es sehr umstaendlich, neue Einheiten hinzuzufuegen.

    Wieso?
    Kann man leicht über eine Überladene Funktion bzw. ein spezialisiertes Template dispatchen.

    Oder meinst du Einheiten-Typen?

    Auch da sehe ich nicht wirklich ein Problem.
    Im Prinzip kann man alle Operationen die man auf allen Einheiten ausführen will über ein "Mach-das-für-alle-Einheiten" Template dispatchen.

    Man gewinnt damit vermutlich keinen Schönheitswettbewerb, aber "sehr umstaendlich" halte ich für übertrieben.



  • hustbaer schrieb:

    @Sone
    Dir hat's grad komplett die Kette ausgehängt, oder?

    Nein.

    Falls dein Beweggrund (a) gewesen sein sollte

    Ja, das war er.

    , dann fehlt für mich schon irgendwie die Info was eigentlich das Problem ist.

    Mir nicht. Wieso? Weil der Fehler für die Zielgruppe offensichtlich ist:

    Ich meine für wen schreibt man sowas - für Leute die es selbst schon wissen? Eher nicht...

    Sieh' dir meinen ersten Post an. Damit wollte ich ausdrücken, dass mir nicht genau klar war, wie SeppJs Code gemeint war (zu wenig von beidem, um es klar zuzuordnen). Der zweite war nur an den Kerl gerichtet, der irgendwie die hier nicht so wichtigen Umlaute ins Spiel brachte, kann also getrost ignoriert werden. Mit dem zweiten war auch nur gesagt, dass mir der Fehler für korrekt-gemeinten Code zu groß war.
    Und der Dritte kam aus meiner Abneigung, deinen nicht sehr toleranten Kommentaren ernste Aufmerksamkeit zu schenken, wie ausnahmsweise mal bei diesem hier.
    Dein erster Post spiegelt eigentlich gut wider, was in mir vorging: Sein Code war irgendwie zu korrekt, um Pseudocode zu sein. Im Endeffekt ist es egal, es war nur ein kleiner Code-Schnipsel der dank mir und dir zu einer völlig überflüssigen Diskussion mit weiteren Beleidigungen und Posts geführt hat. Und wenn du doch so viel intelligenter mit der Situation umgehen kannst, dann tu' das, und damit mir und den anderen einen Gefallen.

    Und falls doch eher (b) oder (c), dann halt gefälligst einfach die Fresse.
    Bzw. halt überhaupt die Fresse, du nervst.

    🙂 Nein. Deal with it.



  • @Kolonist

    Wie willst du verhindern dass jemand Blödsinn der Form Spielfigur.health = -4 macht. Minimale Kopplung, maximale Kohäsion sagt ich dazu. Und eindeutige Zuordnung von Verantwortlichkeiten an Klassen. Ich weis nicht wie man dies mit Template machen sollte.


Anmelden zum Antworten