Klassen, die auf klassen zugreifen



  • Hallo, ich bin ganz neu in c++ und beschäftige mich gerade mit classes.

    und zwar habe ich mich gefragt, ob es möglich ist mehrere Klassen auf eine Klasse zugreifen zu lassen.

    folgendes beispiel: ich habe eine Klasse Punkt

    class Punkt{
        int _pos;
        public:
        Punkt(int pos);
        void bewegen(int dir);
      };
    

    mit den jeweiligen Methoden

    jetzt habe ich meherere Punkte, die weitere unterschiedliche Methoden haben.
    wie implementiere ich das?

    So scheint das nicht zu gehen:

    class Punktcolor{
      int _pos;
      int _color;
      public:
      Punktcolor(int pos, int color);
    }
    
    Punktcolor::Punktcolor( int pos, int color){
       Punkt PUNKTcolor(POS);
       _color = color;
    }
    
    Punktcolor newPunkt(0,0x8);
    
    newPunkt.Punkt.bewegen(20);
    

    Ich hoffe es wird klar was ich versuchen will. Quasi eine Hierarchie bilden, wo eine Klasse Eigenschaften und Methoden von anderen Klassen übernehmen kann.

    LG Max



  • Wenn ich dich richtig verstanden habe, hätte ich es spontant so gemacht:

    struct Color
    {
        int r;
        int g;
        int b;
    };
    struct Position
    {
        int x;
        int y;
    };
    class Punkt
    {
        int size;
        Color color;
        Position pos;
    public:
        Punkt( int size, Color color, Position pos ) : size(size), color(color), pos(pos)
        {}
        void set_pos( Position pos )
        {
            this->pos = pos;
        }
    };
    
    void bewegen( Punkt& p, Position pos )
    {
        p.set_pos( pos );
    }
    
    int main()
    {
        Punkt p{ 10, {0,255,0}, {1000,500} };    
        Position neue_pos{ 500, 250 };
        bewegen(p, neue_pos);
    }
    

    Vererbung hat hier nichts verloren. Ein Punktcolor ist kein Punkt.
    Und die bewegen-Funktion würde ich rausziehen, da man ja auch andere Sachen bewegen kann, und nicht nur einen Punkt.



  • Gefällt mir richtig gut.

    Lernt man sowas während der Ausbildung oder in der Praxis? Diese ordentliche Abstraktion. Selbst wenn man sich in Klassen schon etwas auskennt, selbst das "Spontane" eines Auskenner sieht immer besser aus, noch bevor man sich überhaupt Gedanken darüber gemacht hat, wie man es selbst machen würde 😉



  • out schrieb:

    Und die bewegen-Funktion würde ich rausziehen, da man ja auch andere Sachen bewegen kann, und nicht nur einen Punkt.

    Dem Rest stimme ich zu, aber dem hier nicht.

    void bewegen( Punkt& p, Position pos ): Eine Methode mit dieser Signatur kann nur Punkte bewegen, nichts anderes.

    Wenn du die bewegen-Funktion verallgemeinern willst, dann schreib so:

    /*
     *	abstraktes Interface für bewegliche Objekte
     */
    class IBeweglich
    {
    public:
    	virtual void bewegen (const Position & ziel) = 0;
    	virtual ~IBeweglich () {}
    };
    
    /*
     *	Punkt ist ein bewegliches Objekt
     *	-> implementiert die hierfür notwendigen Methoden
     */
    class Punkt : public IBeweglich
    {
    public:
    	void bewegen (const Position & ziel) { .... }
    };
    


  • lemon03 schrieb:

    Gefällt mir richtig gut.

    Lernt man sowas während der Ausbildung oder in der Praxis? Diese ordentliche Abstraktion. Selbst wenn man sich in Klassen schon etwas auskennt, selbst das "Spontane" eines Auskenner sieht immer besser aus, noch bevor man sich überhaupt Gedanken darüber gemacht hat, wie man es selbst machen würde 😉

    😃
    Ne, sowas lernt man in der Ausbildung normal nicht. Eher durchs lesen vieler Forenbeiträge, gute C++ Bücher und Videos (CppCon).
    Dass es Sinn macht, Funktionen, die auf vieles anwendbar sind, herauszuziehen, siehst du nun auch an der C++ Std Lib (std::begin oder seit C++17 std::size ...)

    @Printe: Ich gebe dir Recht. Das habe ich jetzt nicht weiter ausgeführt.



  • Printe schrieb:

    void bewegen( Punkt& p, Position pos ): Eine Methode mit dieser Signatur kann nur Punkte bewegen, nichts anderes.

    Ich hatte in einer ähnlichen Situation ein template genommen, das nicht nur Punkte, sondern alles, was definiert war, bewegen konnte.

    template<typename T>
    void bewegen( T& p, Position pos );
    


  • Printe schrieb:

    out schrieb:

    Und die bewegen-Funktion würde ich rausziehen, da man ja auch andere Sachen bewegen kann, und nicht nur einen Punkt.

    Dem Rest stimme ich zu, aber dem hier nicht.

    void bewegen( Punkt& p, Position pos ): Eine Methode mit dieser Signatur kann nur Punkte bewegen, nichts anderes.

    Wenn du die bewegen-Funktion verallgemeinern willst, dann schreib so:

    /*
     *	abstraktes Interface für bewegliche Objekte
     */
    class IBeweglich
    {
    public:
    	virtual void bewegen (const Position & ziel) = 0;
    	virtual ~IBeweglich () {}
    };
    
    /*
     *	Punkt ist ein bewegliches Objekt
     *	-> implementiert die hierfür notwendigen Methoden
     */
    class Punkt : public IBeweglich
    {
    public:
    	void bewegen (const Position & ziel) { .... }
    };
    

    Kommst du aus der Java-Welt? Man benötigt nicht für jede kleinere Sache ein Interface. Das macht man in C++ nicht. Da ist der Ansatz über eine freie Funktion zu bevorzugen (außer du willst das Ganze eben als Interface nutzen; sprich entsprechendes Verhalten über Basis-Klassen-Pointer aufrufen).
    Freie Funktionen haben eben auch den Vorteil, dass man sie einfach überladen kann. Wie sieht das mit Interfaces aus einer Third-Party-Lib aus, die du gerne nutzen und ändern würdest?



  • anti-freak schrieb:

    Kommst du aus der Java-Welt?

    Nein, ich hab nur mal ne Weile C# gemacht.

    Man benötigt nicht für jede kleinere Sache ein Interface. Das macht man in C++ nicht.
    

    "Das macht man nicht" war mir schon egal, als mir meine Eltern das noch gesagt haben 🙂

    Und ob man es braucht oder nicht, hängt vom Design-Ansatz ab. Hast du polymorphe Klassen, die alle irgendwie "beweglich" sind, dann ist ein Interface dafür nicht die schlechteste Idee.

    Wie sieht das mit Interfaces aus einer Third-Party-Lib aus, die du gerne nutzen und ändern würdest?

    Wenn die Thirdparty-Lib eine Grafik-Lib ist, die alles verschieben kann, was IMovable implementiert - warum denn nicht?



  • Printe schrieb:

    "Das macht man nicht" war mir schon egal, als mir meine Eltern das noch gesagt haben 🙂

    Ohne jetzt auf spezifische Programmierkenntnisse einzugehen, aber das war ein Fehler 🙂

    Wenn nicht heute, irgendwann wirst Du es edit: ihn bereuen... 😉



  • Printe schrieb:

    anti-freak schrieb:

    Kommst du aus der Java-Welt?

    Nein, ich hab nur mal ne Weile C# gemacht.

    Man benötigt nicht für jede kleinere Sache ein Interface. Das macht man in C++ nicht.
    

    "Das macht man nicht" war mir schon egal, als mir meine Eltern das noch gesagt haben 🙂

    Und ob man es braucht oder nicht, hängt vom Design-Ansatz ab. Hast du polymorphe Klassen, die alle irgendwie "beweglich" sind, dann ist ein Interface dafür nicht die schlechteste Idee.

    Wie sieht das mit Interfaces aus einer Third-Party-Lib aus, die du gerne nutzen und ändern würdest?

    Wenn die Thirdparty-Lib eine Grafik-Lib ist, die alles verschieben kann, was IMovable implementiert - warum denn nicht?

    Gut, du weißt es besser. Ich hab jetzt nicht sonderlich große Lust mit dir darüber zu diskutieren. Beschäftige dich mit dem Thema, oder bleib bei deiner Meinung, ist mir letztlich gleich. Dachte nur, ich bring dich da nochmal zum drüber Nachdenken.
    Wie ich gesagt habe, ist es OK sowas zu machen, wenn man es braucht. Das Ganze aber als DIE LÖSUNG schlechthin verkaufen zu wollen, ist einfach falsch. Vll war das deutlich genug. Inheritance ist nichts, womit man leichtfertig um sich schmeißen sollte. Mit irgendwelchen Klassen VTable zu generieren, nur weil man es kann, ist sicherlich auch nicht die performanteste Idee.



  • anti-freak schrieb:

    Wie ich gesagt habe, ist es OK sowas zu machen, wenn man es braucht.

    Ja. Das sagte ich auch. Zitat: "Und ob man es braucht oder nicht, hängt vom Design-Ansatz ab."

    Das Ganze aber als DIE LÖSUNG schlechthin verkaufen zu wollen, ist einfach falsch.

    Da hast du völlig recht. Das tat ich aber auch nicht. Oder findest du irgendwo ein solches Zitat von mir?

    Inheritance ist nichts, womit man leichtfertig um sich schmeißen sollte.

    Drölfzig Überladungen derselben freien Funktionen aber auch nicht.

    Mit irgendwelchen Klassen VTable zu generieren, nur weil man es kann, ist sicherlich auch nicht die performanteste Idee.

    Und welchen Vorteil bieten deine "freien Überladungen"? Warum soll ich

    bewegen (punkt, pos);
    

    aufrufen, wenn ich genausogut direkt

    punkt.set_pos (pos);
    

    sagen kann? Das ist doch nur syntaktischer Zucker, den man mit einem zusätzlichen Funktionsaufruf bezahlt. Also das Musterbeispiel für Imperformanz.



  • Printe schrieb:

    anti-freak schrieb:

    Wie ich gesagt habe, ist es OK sowas zu machen, wenn man es braucht.

    Ja. Das sagte ich auch. Zitat: "Und ob man es braucht oder nicht, hängt vom Design-Ansatz ab."

    Das Ganze aber als DIE LÖSUNG schlechthin verkaufen zu wollen, ist einfach falsch.

    Da hast du völlig recht. Das tat ich aber auch nicht. Oder findest du irgendwo ein solches Zitat von mir?

    Printe schrieb:

    Dem Rest stimme ich zu, aber dem hier nicht.

    void bewegen( Punkt& p, Position pos ): Eine Methode mit dieser Signatur kann nur Punkte bewegen, nichts anderes.

    Wenn du die bewegen-Funktion verallgemeinern willst, dann schreib so:

    Das klingt schon sehr danach.

    Printe schrieb:

    anti-freak schrieb:

    Inheritance ist nichts, womit man leichtfertig um sich schmeißen sollte.

    Drölfzig Überladungen derselben freien Funktionen aber auch nicht.

    Was spricht dagegen?

    Printe schrieb:

    anti-freak schrieb:

    Mit irgendwelchen Klassen VTable zu generieren, nur weil man es kann, ist sicherlich auch nicht die performanteste Idee.

    Und welchen Vorteil bieten deine "freien Überladungen"? Warum soll ich

    bewegen (punkt, pos);
    

    aufrufen, wenn ich genausogut direkt

    punkt.set_pos (pos);
    

    sagen kann? Das ist doch nur syntaktischer Zucker, den man mit einem zusätzlichen Funktionsaufruf bezahlt. Also das Musterbeispiel für Imperformanz.

    Verstehe deine Aussage nicht ganz. Du sagst, dass ein zur Runtime ausgewerteter Funktionsaufruf dem einer Compiletime ausgewerteter ebenbürtig ist? Dann solltest du nochmal ein Buch zu Rate ziehen. Das ist weder äquivalent, noch ist es annähernd gleich effektiv.
    Wo genau ist der Vorteil? Um bei deinem Beispiel zu bleiben, eine GUI Lib hat für gewöhnlich mehrere Widgets. Diese Widgets haben größtenteils das selbe Verhalten, wenn man sie bewegt (erweitern es allerdings teilweise, daher ist virtual hier angebracht). Du schlägst nun aber vor, dass ein Punkt und sonstige X-beliebige Klassen vom gleichen Interface erben, aber dennoch komplett unterschiedliches Verhalten haben. Das einzige was das macht, ist die Kopplung zwischen deinen Klassen erhöhen.

    EDIT: Kann es sein, dass du da irgendwas durcheinander schmeißt? Generell ist es natürlich ok, Member Funktionen zu nutzen, sofern nötig. Viele C++ Entwickler präferieren allerdings freie Funktionen über member Funktionen (sofern nur auf public Dinge zugegriffen wird), eben auf Grund der Möglichkeit der Überladung.
    Du scheinst dir allerdings nicht ganz im Klaren darüber zu sein, dass eine als virtual deklarierte Funktion einen deutlichen Overhead gegenüber freien oder "normalen" Memberfunktionen mitsich bringt.



  • anti-freak schrieb:

    Printe schrieb:

    Wenn du die bewegen-Funktion verallgemeinern willst, dann schreib so:

    Das klingt schon sehr danach.

    Ein Wenn-dann-Satz klingt für dich, als wollte ich etwas als "die Lösung schlechthin" verkaufen? Aha ...

    Übrigens war das meine Antwort auf diese Aussage: "Und die bewegen-Funktion würde ich rausziehen, da man ja auch andere Sachen bewegen kann, ...". Ein bisschen Kontext tut dem Verständnis manchmal gut ...

    Drölfzig Überladungen derselben freien Funktionen aber auch nicht.

    Was spricht dagegen?

    Was spricht denn dafür? Polymorph wirds dadurch nicht, und auch sonst erkenne ich keinen Vorteil.

    Printe schrieb:

    Das ist doch nur syntaktischer Zucker, den man mit einem zusätzlichen Funktionsaufruf bezahlt. Also das Musterbeispiel für Imperformanz.

    Verstehe deine Aussage nicht ganz. Du sagst, dass ein zur Runtime ausgewerteter Funktionsaufruf ...

    Ein Pointer, der in einer Tabelle steht und auf ein Stück Code zeigt. Ein Umweg von einem Schritt. Der Vorteil, dass man dadurch Polymorphie gewinnt, kann diesen Umweg mehr als wert sein. (Beachte: ich schreibe "... kann ... wert sein").

    ... dem einer Compiletime ausgewerteter ebenbürtig ist?

    Eine Funktion, die absolut nichts tut, außer "punkt.set_pos(pos)" hinter "setPosition (punkt, pos)" zu verstecken. Eine "hübsche" (?) Tapete, nichts weiter, das meine ich mit "Syntaxzucker". Diese Funktion erfüllt keinerlei Zweck und verschwendet einen ganzen Stackframe (Argumente pushen, call, return) für nichts. Ich verstehe nicht, wie man da auf "Performance" pochen kann.



  • Polymorphie heißt nicht, dass man alles virtual deklarieren muss. Du bist nicht bereit über den Tellerrand hinaus zu schauen, deswegen ist jetzt für mich hier Schluss. Das Ganze sollte als Anmerkung verstanden werden, nicht als Diskussionsgrundlage. Wenn du nicht verstehst, worauf ich hinaus will, dann mach einen neuen Thread auf und lass es dir erklären.
    Mein Statement ist und bleibt, runtime Polymorphie ist nicht das Allheilmittel.

    Eins kann ich so nicht stehen lassen:

    Eine Funktion, die absolut nichts tut, außer "punkt.set_pos(pos)" hinter "setPosition (punkt, pos)" zu verstecken. Eine "hübsche" (?) Tapete, nichts weiter, das meine ich mit "Syntaxzucker". Diese Funktion erfüllt keinerlei Zweck und verschwendet einen ganzen Stackframe (Argumente pushen, call, return) für nichts. Ich verstehe nicht, wie man da auf "Performance" pochen kann.

    Wenn dein Objekt eine set_pos Methode anbietet, die genau das macht, was du möchtest, dann ist diese sehr wohl zu benutzen. Dir ging es darum, ein einheitliches Interface zu erstellen. Dafür benutzt du deine Runtime Polymorphie und ich hab dazu compiletime Polymorphie in den Raum geschmissen (via overlaod). Im übrigen werden solche calls zu 99% wegoptimiert, VTable lookups nicht.

    EDIT: Im übrigen geht es mir hier nicht explizit um Performance. Es ist nur eines der Argumente.



  • anti-freak schrieb:

    ich hab dazu compiletime Polymorphie in den Raum geschmissen (via overlaod).

    Lach. Alle, die overloads als compile time polymorphism bezeichnen, setzen das in Gänsefüßchen, mit voller Absicht und aus gutem Grund, denn das ist nur scheinbar Polymorphie. Nur du nimmst das für bare Münze.

    Im übrigen werden solche calls zu 99% wegoptimiert

    Na dann sind sie ja wenigstens wieder unschädlich.


Log in to reply