Pointer & Klassen



  • //aptr=vptr; // nicht erlaubt

    Hmm..

    Warum steht dann in der Literatur:

    "Ein Zeiger (oder eine Referenz), der auf ein Objekt einer abgeleiteten Klasse weist, kann in einen Zeiger auf ein Objekt der Basisklasse umgewandelt werden. Dies entspricht der Standardkonvertierung, die auch ohne explizite Typumwandlung erlaubt ist."



  • Da hast du recht aber nicht umgekehrt ( wie in dem Fall ).
    Kannst dir ungefähr so vorstellen
    ein schlafzimmer ist ein zimmer. aber ein zimmer muss nicht unbedingt ein schlafzimmer sein.



  • AJ_Styles schrieb:

    Visual C++ 6 gibt keine Warnung dafür aus.

    Visual C++ 6 ist ja auch kein Compiler. Und dass der darin enthaltene Compiler (MSC) nicht mehr ganz taufrisch ist, da gabs auch schon genügend Beiträge zu. Vielleicht hilft auch, wenn das Warnlevel einfach ganz rauf gesetzt wird.

    AJ_Styles schrieb:

    Vector *vptr=&Vector(10,20,30);
        Abg *aptr=&Abg(1,2,3);
    

    Warum handelt es sich denn hierbei um temporäre Objekte und nicht um ganz normale lokale Objekte?

    Um lokale Objekte handelt es sich trotzdem, nur die Lebensdauer ist eine andere als "normal" erzeugte Variablen/Objekte. Die Lebensdauer endet dann, wenn die Anweisung vollständig abgearbeitet ist. Und wie ich bereits sagte, bedeutet dies praktisch, dass das temporäre Objekt zerstört werden kann, wenn es als rvalue nicht mehr gebraucht wird.

    AJ_Styles schrieb:

    Meinst du ich kann einfach aptr=vptr schreiben?
    Dann bekomme ich aber folgende Fehlermeldung:

    error C2440: '=' : 'class Vector *' kann nicht in 'class Abg *' konvertiert werden

    Nein. Implizit funktioniert das nur in eine Richtung, nämlich von unten nach oben, dh von abgeleiteter Klasse zur Basisklasse. Für die andere Richtung musst du schon mehr machen. Dann brauchst du einen Cast.

    aptr = static_cast<Abg*>(vptr);
    

    Das solltest du aber nur machen, wenn du dir wirklich sicher bist, dass du einen Basisklassenzeiger auf das abgeleitete Objekt (Abg) hast. Ansonsten schaust du dir mal dynamic_cast an. Oder noch besser, du überlegst dir erstmal, ob du diesen Downcast überhaupst brauchst oder nicht doch eine elegantere Lösung findest.

    AJ_Styles schrieb:

    Hab das jetzt so gemacht:
    [...]

    Der Code ist bis auf das angesprochene aptr=vptr ok. Globale Variablen wollen wir aber nur im äussersten Notfall. Und solch einer liegt hier nicht vor. Also definieren wir v und a in der main Funktion.

    AJ_Styles schrieb:

    Allerdings wird, wenn ich die Methode test() aufrufe, trotzdem die Methode der abgeleiteten Klasse aufgerugen, trotz virtual-Deklaration.

    Was völlig normal ist, virtual verdeckt nicht sondern überschreibt.



  • Danke nochmal.

    Ich habe noch Probleme mit einigen Formulierungen:

    Um noch mal auf meine Literaturquelle zurückzukommen:

    "Ein Zeiger (oder eine Referenz), der auf ein Objekt einer abgeleiteten Klasse weist, kann in einen Zeiger auf ein Objekt der Basisklasse umgewandelt werden. Dies entspricht der Standardkonvertierung, die auch ohne explizite Typumwandlung erlaubt ist."

    Aber genau das funktioniert ja nicht. aptr, der Zeiger auf die abgeleitete Klasse kann nicht in vptr, einen Zeiger auf die Basisklasse umgewandelt werden?!

    Das solltest du aber nur machen, wenn du dir wirklich sicher bist, dass du einen Basisklassenzeiger auf das abgeleitete Objekt (Abg) hast. Ansonsten schaust du dir mal dynamic_cast an. Oder noch besser, du überlegst dir erstmal, ob du diesen Downcast überhaupst brauchst oder nicht doch eine elegantere Lösung findest.

    Wieso spricht man in diesem Zusammenhang von einem Downcast? Upcast wäre doch logischer, da man einen Zeiger einer abgeleitete Klasse auf eine Basisklasse zeigen lässt.



  • AJ_Styles schrieb:

    "Ein Zeiger (oder eine Referenz), der auf ein Objekt einer abgeleiteten Klasse weist, kann in einen Zeiger auf ein Objekt der Basisklasse umgewandelt werden. Dies entspricht der Standardkonvertierung, die auch ohne explizite Typumwandlung erlaubt ist."

    Aber genau das funktioniert ja nicht. aptr, der Zeiger auf die abgeleitete Klasse kann nicht in vptr, einen Zeiger auf die Basisklasse umgewandelt werden?!

    Du wandelst nicht aptr in vptr um sondern

    aptr=vptr; // nicht erlaubt
    

    wandelt vptr in aptr um. ( aus vptr soll ein aptr werden ) 😉



  • AJ_Styles schrieb:

    "Ein Zeiger (oder eine Referenz), der auf ein Objekt einer abgeleiteten Klasse weist, kann in einen Zeiger auf ein Objekt der Basisklasse umgewandelt werden. Dies entspricht der Standardkonvertierung, die auch ohne explizite Typumwandlung erlaubt ist."

    Aber genau das funktioniert ja nicht. aptr, der Zeiger auf die abgeleitete Klasse kann nicht in vptr, einen Zeiger auf die Basisklasse umgewandelt werden?!

    Das solltest du aber nur machen, wenn du dir wirklich sicher bist, dass du einen Basisklassenzeiger auf das abgeleitete Objekt (Abg) hast. Ansonsten schaust du dir mal dynamic_cast an. Oder noch besser, du überlegst dir erstmal, ob du diesen Downcast überhaupst brauchst oder nicht doch eine elegantere Lösung findest.

    Wieso spricht man in diesem Zusammenhang von einem Downcast? Upcast wäre doch logischer, da man einen Zeiger einer abgeleitete Klasse auf eine Basisklasse zeigen lässt.

    Ich glaube, du hast noch einige Probleme mit dem Verständnis, was zu was umgewandelt wird.

    a = b;
     <--
    

    Links steht das Ziel, muss ein sogenannter lvalue sein. Und rechts steht die Quelle, also der rvalue. (wobei b trotzdem auch ein lvalue sein kann)
    Sollte also b nicht direkt a zugewiesen werden können, muss eine Umwandlung von Typ(b) nach Typ(a) erfolgen.

    Wenn da also steht

    vptr = aptr;
    

    dann wird aptr zu vptr umgewandelt, also der Zeiger der abgeleiteten Klasse in einen Zeiger der der Basisklasse.



  • Sorry das ich euch nochmal nerve:

    Wenn da also steht

    vptr = aptr;

    dann wird aptr zu vptr umgewandelt, also der Zeiger der abgeleiteten Klasse in einen Zeiger der Basisklasse.

    Aber wieso kann ich, nach obiger Typumwandlung, aptr trotzdem nicht auf eine Instanz der Basisklasse zeigen lassen, wo aptr doch nun ein Basisklassenzeiger sein sollte?

    aptr=&v //Fehler

    Und wieso wird, wenn ich jetzt über vptr die Methode test() aufrufe, die Methode der abgeleiteten Klasse aufgerufen? Ich denke, ich habe aptr in vptr umgewandelt und nicht umgekehrt (was ja auch ohne expliziten cast nicht gehen würde)? 😕



  • Ich denke, ich habe aptr in vptr umgewandelt

    Du hast überhaupt nichts umgewandelt ( im sinne von Verändert ).
    Ein Zeiger vom typ der Basisklasse zeigt jetzt auf ein Object der abgeleiteten Klasse. Umgekehrt ist das nicht möglich ein Zeiger vom Typ einer Abgeleiteten Klasse kann ( ohne expliziten cast ) nicht auf ein Object vom typ der Basisklasse zeigen. Das kann ja auch nicht sein da der Basisklasse ja Attibute der abgeleiteten Klasse fehlen könnten, eine abgeleitete Klasse erbt aber immer alle Attribute der Basisklasse also ist auch kein cast notwendig.

    Und wieso wird, wenn ich jetzt über vptr die Methode test() aufrufe, die Methode der abgeleiteten Klasse aufgerufen?

    Der einzige Sinn von Polymorphie ist es ja, dass über einem Zeiger vom typ der Basisklasse eben auf die virtuellen Methoden der konkreten (abgeleiteten) Klasse zugegriffen wird (late binding).
    Kurt



  • AJ_Styles schrieb:

    Aber wieso kann ich, nach obiger Typumwandlung, aptr trotzdem nicht auf eine Instanz der Basisklasse zeigen lassen, wo aptr doch nun ein Basisklassenzeiger sein sollte?

    Nein, aptr ist kein Basisklassenzeiger. Ich erinnere dich nochmal an die Deklaration

    Abg* aptr
    

    AJ_Styles schrieb:

    Und wieso wird, wenn ich jetzt über vptr die Methode test() aufrufe, die Methode der abgeleiteten Klasse aufgerufen?

    Das hab ich dir doch schon beantwortet.

    virtual verdeckt nicht sondern überschreibt

    Die Funktion ist nunmal virtuell. Wenn du also willst, dass die Funktion der Basisklasse aufgerufen wird, dann musst du auch ein Objekt der Basisklasse instanzieren.

    Vielleicht solltest du dir das ganze mal etwas bildlich vorstellen.

    ----------------------------------------
    |              Abg Objekt              |
    |--------------------------------------|
    | Vector     | vtable     | Abg only   |
    |------------|------------|------------|
    | x          | test()     | ctor       |
    | y          |            | ...        |
    | z          |            |            |
    | ctor       |            |            |
    | ...        |            |            |
    ----------------------------------------
    

    Du instanzierst also ein Objekt des Typs Abg. Dieses kannst du dir bildlich wie mein ASCII-Art 🙂 vorstellen. Da wäre der Teil der Basisklasse (Vector), die vtable und der Teil der in Abg noch hinzugekommen ist (Abg only). In der vtable werden die Adressen der virtuellen Funktionen abgespeichert. Hat deine Klasse keine virtuellen Funktionen, so kann der Teil auch wegfallen. Bei der Instanzierung wird dort also die Adresse von Abg::test abgelegt.
    Wenn du jetzt einen Zeiger auf dieses Objekt hast (Abg*) und wandelst diesen in einen Zeiger der Basisklasse um (Vector*), dann zeigt dieser auf den Vector Teil. Rufst du jetzt test() auf, sieht der Compiler, dass es eine virtuelle Funktion ist (so hast du diese ja schliesslich in Vector deklariert) und schaut dementsprechend in die vtable. Dort steht immer noch Abg::test und diese Funktion ruft er dann auch auf.
    Und genau darin liegt ja auch der Witz von virtual, du hast also eine Referenz oder einen Zeiger auf die Basisklasse, es wird aber trotzdem die Funktion des eigentlich instanzierten Objektes, nämlich der abgeleiteten Klasse, aufgerufen.



  • Danke nochmal euch beiden! Ihr seid eine wirklich große Hilfe!

    Also eine Zusammenfassung:

    Wenn ich schreibe

    aptr=vptr
    

    ;

    Ich möchte, dass mein Zeiger aptr (der auf eine abgeleitete Klasse zeigt) auf das Objekt zeigt, dessen Adresse in vptr gespeichert ist (in diesem Fall also eine Instanz der Basisklasse Vektor).Da beide Typen nicht kompatibel sind, muss vptr zuvor per static_cast in ein Zeiger auf die abgeleitete Klasse umgewandelt werden. Der Cast ist notwendig, da sich in der abgeleiteten Klasse ja andere (zusätzliche) Elemente als in der Basisklasse befinden können.
    Umgekehrt ist kein cast nötig, da der Compiler dies implizit durchführen kann (sofern public-Vererbung vorliegt).

    Kann man das so stehen lassen?

    Das mit der virtuellen Tabelle und der späten Bindung wurde auch sehr gut erklärt.

    Eine Sache wundert mich dennoch:

    Wenn du jetzt einen Zeiger auf dieses Objekt hast (Abg*) und wandelst diesen in einen Zeiger der Basisklasse um (Vector*), dann zeigt dieser auf den Vector Teil.

    Das heisst eigentlich zeigt der Pointer gar nicht auf eine Instanz der Basisklasse, sondern vielmehr auf den Vector-Teil im abgeleiteten Objekt?


Anmelden zum Antworten