Vererbung



  • Skym0sh0 schrieb:

    Nathan schrieb:

    Beides ist falsch, Slicing.

    Nehmen wir an, wir sprechen hier mal grad für einen Momen Java: Wo wäre das dann falsch?

    Wir sind hier aber in C++, und eine explizite Zuweisung von Objekten führt zu Slicing. Und Slicing ist doof (s.u.).

    Verbung bildet eine "Ist-Ein" Beziehung ab. z.B. ein Kreis ist ein GeometrischesObjekt.

    Jupp, und ein Quadrat ist ein Rechteck...

    Edit: @vatan:
    Wenn mit virtual überschrieben und Referenzen/Zeiger, die aus B.
    Wenn mit virtual überschrieben und Objekte, die aus A (Slicing!).
    Wenn ohne virtual überschrieben, immer A.



  • Welche Methode ausgeführt wird, ist abhängig davon was die Variablen darstellen, und was daher die Zuweisung macht.
    Wenn die Variablen direkt die Objekte darstellen, dann ist x nach der Zuweisung immer noch ein A , und daher wird A::i ausgeführt.

    Wenn die Variablen nur Verweise (*) auf Objekte darstellen, dann verweist x nach der Zuweisung auf das selbe Objekt wie y , also ein B .
    Dann wiederrum ist es abhängig davon ob die Funktion i virtual ist oder nicht. Wenn sie virtual ist, dann wird B::i ausgeführt. Sonst auch wieder A::i , nur halt auf einem B Objekt.

    *: Mit "Verweis" meine ich hier nicht ein C++ "reference" -- weswegen ich auch nicht das Wort "Referenz" oder "reference" verwendet habe. Ich meine damit sowas wie Zeiger in C++, oder Referenzen in Java oder C#.
    Bei denen wird bei der Zuweisung dann nur der Verweis geändert, die Objekte selbst bleiben unverändert.



  • Nathan schrieb:

    Wir sind hier aber in C++, und eine explizite Zuweisung von Objekten führt zu Slicing.
    ...
    Wenn mit virtual überschrieben und Referenzen/Zeiger, die aus B.

    Wenn es um C++ geht, und x eine Referenz auf ein A ist, dann kommt es hier auch zu Slicing.
    Es wird nach der Zuweisung also genau so A::i aufgerufen, selbst wenn i virtual ist.

    Vorausgesetzt natürlich das vollständige Objekt auf das x ursprünglich verweisen hat war auch wirklich ein A, aber das ist ja so angegeben.



  • laut prof wird die Methode in der Nachfahrenklasse also von B aufgerufen. Was allerdings in Widerspruch mit seinem Skript steht, dort ist vermerkt, dass die Methode immer aus der Klasse aufgerufen wird, in der es erzeugt wurde.

    Und dass dort Informationen abgeschnitten werden, hat er nicht erwähnt.



  • hustbaer schrieb:

    Nathan schrieb:

    Wir sind hier aber in C++, und eine explizite Zuweisung von Objekten führt zu Slicing.
    ...
    Wenn mit virtual überschrieben und Referenzen/Zeiger, die aus B.

    Wenn es um C++ geht, und x eine Referenz auf ein A ist, dann kommt es hier auch zu Slicing.

    Ups, hab das "nach dieser Zuweisung" überlesen.



  • vatan89 schrieb:

    laut prof wird die Methode in der Nachfahrenklasse also von B aufgerufen. Was allerdings in Widerspruch mit seinem Skript steht, dort ist vermerkt, dass die Methode immer aus der Klasse aufgerufen wird, in der es erzeugt wurde.

    Und dass dort Informationen abgeschnitten werden, hat er nicht erwähnt.

    Ist das eine C++ Klasse/Vorlesung oder geht's um eine andere Sprache? Oder gar um objektorientiertes Programmieren im Allgemeinen?

    ps: diese Frage wäre vermutlich in "Rund um die Programmierung" besser aufgehoben.



  • Nathan schrieb:

    Beides ist falsch, Slicing.

    Na und?

    class graphicalObject{};
    class circle : public graphicalObject{};
    
    graphicalObject go;
    circle c;
    
    go = c;
    

    sollte doch keine Probleme verursachen und richtig sein in dem Sinne, dass ich jetzt mit go ganz normal weiter arbeiten kann?!

    Umgekehrt ist das aber (ohne Definition eines passenden Zuweisungsoperators) falsch, weil die Membervariablen nicht gefüllt werden können?!



  • [quote="Skym0sh0"]

    int main()
    {
        GeometryObject go;
        Circle c;
        Rectangle r;
        //---------------------
        GeometryObject & cref = c; // geht ohne Problem, wie dein Prof sagt
        GeometryObject & rref = r; // weil eine Kreis/Rechteck ein GeometryObject ist
    
        Circle & refC = c; // geht
        Circle & refR = r; // geht nicht, denn ein Rechteck ist kein Kreis
        Circle & refFail = go; // geht auch nicht, denn niemand garantiert, dass go eine Instanz von Kreis ist.
    }
    
    GeometryObject go;
        Circle c;
        Rectangle r;
        //---------------------
        GeometryObject & cref = c; // go <- c
        GeometryObject & rref = r; // go <- r
    
        Circle & refC = c; // c <- c
        Circle & refR = r; // c <- r // das kann ja nicht gehen 
        Circle & refFail = go; // und hier versuchtst du dem c Objekt go zuzuweisen ja? c <- go und das kann nicht funktionieren wegen Slicing oder generell unmöglich?
    

  • Mod

    Belli schrieb:

    Nathan schrieb:

    Beides ist falsch, Slicing.

    Na und?

    class graphicalObject{};
    class circle : public graphicalObject{};
    
    graphicalObject go;
    circle c;
    
    go = c;
    

    sollte doch keine Probleme verursachen und richtig sein in dem Sinne, dass ich jetzt mit go ganz normal weiter arbeiten kann?!

    Umgekehrt ist das aber (ohne Definition eines passenden Zuweisungsoperators) falsch, weil die Membervariablen nicht gefüllt werden können?!

    Dir muss schon klar sein, auf welcher gedanklichen Ebene die Frage angesiedelt sein soll.

    1. Sprachlich

    class graphicalObject{};
    class circle : public graphicalObject{};
    
    graphicalObject go;
    circle c;
    
    go = c;
    c = go;
    

    sind syntaktisch Zuweisungen. Also muss der Compiler schauen ob es entweder einen passenden eingebauten Operator dafür gibt (offenbar nicht der Fall) oder eben eine passend Operatorüberladung existiert. Im Falle des Zuweisungsoperators muss dieser stets als nicht-statische Memberfunktion in der Klasse deklariert sein, die zum linken Operanden (statischer Typ) gehört.
    Da keine zusätzlichen Überladungen von uns deklariert wurden, hat der Compiler bereits implizit folgende Operatoren deklariert:

    graphicalObject& graphicalObject::operator=(const graphicalObject&);
    circle& circle::operator=(const circle&);
    

    Bei der Zuweisung

    go = c;
    

    muss der Compiler also überprüfen, ob der Zuweisungsoperator

    graphicalObject& graphicalObject::operator=(const graphicalObject&)
    

    mit dem Argument c (lvalue des Typs circle) aufgerufen werden kann. Und das ist möglich, weil
    1. circle von graphicalObject abgeleitet ist
    2. diese Ableitung öffentlich ist, und
    3. in c genau ein Basisklassensubobjekt des Typs graphicalObject existiert (keine Mehrfachvererbung).
    Innerhalb des vom Compilers generierten Zuweisungsoperators werden alle korrespondierenden Basisklassen und Member (von graphicalObject, hier: keine) paarweise zugewiesen.

    Im Falle von

    c = go;
    

    ist der Kandidat für die Zusweisung

    circle& circle::operator=(const circle&);
    

    aber das Argument go des Typs graphicalObject kann nicht an eine Referenz des Typs circle gebunden werden. Da keine andree passene Überladung des Zuweisungsoperators existiert, muss diese Zuweisung fehlschlagen.

    2. Logisch
    Das Ergebnis einer Zuweisung sollte ein Objekt sein, dessen Zustand äquivalent zu dem des ursprünglichen Objektes ist.
    In

    go = c;
    

    ist das offenbar nicht der Fall, denn das Objekt go ist auch nach der Zuweisung kein circle-Objekt, sondern eben nur eine Kopie des in c enthaltenen Teilobjektes.

    Belli schrieb:

    class graphicalObject{};
    class circle : public graphicalObject{};
    
    graphicalObject go;
    circle c;
    
    go = c;
    

    sollte doch keine Probleme verursachen und richtig sein in dem Sinne, dass ich jetzt mit go ganz normal weiter arbeiten kann?!

    Niemand hier weiss, was für dich normal und problemlos ist. Das hat offenbar etwas damit zu tun, wass du von dem Code erwartest.



  • Tja, da hast Du sehr schön, ausführlich und verständlich dargelegt, was ich mit einem einzigen Satz abgetan habe.
    Und mir war natürlich klar, dass nach

    go = c;
    

    go kein circle ist und sein kann ... und eigentlich hätte ich erwartet, dass das jedem klar ist, schließlich ist und bleibt go vom Typ graphicalObject.
    Für mich ist diese Zuweisung im Sinne des Fragestellers richtig, weil ich danach mit meinem go machen kann, was ich mit jedem Objekt vom Typ graphicalObject machen kann.
    Natürlich ist klar, dass es zu Slicing kommt, ich halte das aber nicht für relevant, weil mein Objekt go genau so aussieht, als wenn ich ihm die entsprechenden Attribute von c durch Initialisierung oder Memberfunktionen mitgegeben hätte, statt durch Zuweisung von c ...
    Also, es muss natürlich klar sein, dass go ein graphicalObject ist und bleibt, deswegen funktioniert der umgekehrte Weg ja auch nicht.



  • Im Falle von

    c = go;
    

    ist der Kandidat für die Zusweisung

    circle& circle::operator=(const circle&);
    

    aber das Argument go des Typs graphicalObject kann nicht an eine Referenz des Typs circle gebunden werden. Da keine andree passene Überladung des Zuweisungsoperators existiert, muss diese Zuweisung fehlschlagen.

    ok, das habe ich etwas verstanden. Könntest du genau das noch einmal weniger fachlich erklären, mit ganz banalen Worten ohne Überladung und Zuweisungsoperator. Wäre das möglich :S

    Ich weiß ich streng euch etwas an aber ich würde das gerne zu 100% verstehen und ich bin nah dran hab ich das Gefühl


Anmelden zum Antworten