vtable



  • es muss zwischen

    derived::~derived()
    derived::~base()
    

    eine Beziehung geben, sonst wird derived::~base() nicht automatisch aufgerufen wenn du hier delete bp aufrufst(implizit derived::~derived() aufrufen)



  • Es gibt kein "derived::~base();".

    Das ganze läuft so:

    base* b = new derived();
    delete b; // ruft Destruktor über "b->_vfptr[2](...)", also "b->~derived(...)" auf.
    
    // intern sieht der Destruktor dann ungefähr so aus:
    
    derived::~derived (...)
    {
       // eigener Code, Speicherbereinigung etc.
       base::~base();
    }
    


  • wenn es so ist, dann steht base::base nicht auf der Vtable von derived, richtig?



  • richtig



  • wenn ich ein derived class noch eine virtual funktion hinzufüge

    virtual foo3();
    

    dann soll es in der vtable so aussehen:

    bei 'derived':
    derived::foo()
    base::foo2()
    derived::foo3()
    derived::~derived()
    

    und bei

    base *bp = new derived;
    

    aber ich zur Laufzeit einen Zeiger erzeugt, der auf diese Table zeigt.
    dann soll ich auch kein problem

    bp->foo3();
    

    aufrufen ohne downcast. Aber in der Praxis muss ich immer

    static_cast<derived*>(bp)->foo3();
    

    schreiben, wieso??



  • Ganz einfach, weil foo3 nicht in der Basisklasse definiert ist. Du kannst so nur das aufrufen was auch in der Basisklasse definiert ist. Wenn du also auch foo3 mit einem Zeiger auf base aufrufen willst, musst du es auch in der Basisklasse definieren. Wenn du etwas ohne cast aufrufen willst musst du es auch definiert haben. Die Basisklasse kann ja nicht etwas kennen, was erst in einer abgeleiteten Klasse vorkommt.



  • Ganz einfach, weil foo3 nicht in der Basisklasse definiert ist. Du kannst so nur das aufrufen was auch in der Basisklasse definiert ist. Wenn du also auch foo3 mit einem Zeiger auf base aufrufen willst, musst du es auch in der Basisklasse definieren. Wenn du etwas ohne cast aufrufen willst musst du es auch definiert haben. Die Basisklasse kann ja nicht etwas kennen, was erst in einer abgeleiteten Klasse vorkommt.

    nach der Theorie von vorhin muss es doch möglich sein.
    wir reden jetzt über die beiden vtables
    Zur Compilerzeit wird 2 vtables erzeugt, also statisch, existiert zu diesem Zeitpunkt noch keiner Zeiger zu den beiden Tabellen.
    vtable von base:

    table 1 bei 'base':
    [0] base::foo()
    [1] base::foo2()
    [2] base::~base()

    table2 bei 'derived':
    [0] derived::foo()
    [1] base::foo2()
    [2] derived::foo3()
    [3] derived::~derived()

    bei Aufruf:

    base *bp = new derived;
    

    wird ein Zeiger this erzeugt, was auf table2 zeigt.Durch diesen Zeiger kann ich natürlich auch foo3()aufrufen, etwa this[2]->foo3();



  • Welche Funktionen wirklich zur Verfügung stehen, steht aber erst zur Laufzeit fest. Dein Compiler guckt nicht nach welche Funktionen er aufrufen kann, sondern welche definiert sind. Wenn du jetzt statt derived eine derived2-Klasse hast und diese hat foo4, nicht aber foo3 würde der aufruf nicht mehr klappen. Und wenn du sowas über ein switch-case-Konstrukt oder so entscheidest welche abgeleitete Klasse verwendet werden soll kann das nicht funktionieren, weil der Compiler das nicht zur Compile-Time auswerten kann. Dann musst du explizit den Typ angeben.


  • Mod

    netrobot schrieb:

    vtable von base:

    table 1 bei 'base':
    [0] base::foo()
    [1] base::foo2()
    [2] base::~base()

    table2 bei 'derived':
    [0] derived::foo()
    [1] base::foo2()
    [2] derived::foo3()
    [3] derived::~derived()

    die einträge der vtable der abgeleiteten klasse müssen nat. exakt mit denen der basisklasse korrespondieren. denn ein virtueller funktionsaufruf vberuht ja gerade darauf, dass ein bestimmter eintrag, welcher einer bestimmten virtuellen funktion zugeordnet ist, aufgerufen wird. folglich:

    [0] derived::foo()
    [1] base::foo2()
    [2] derived::~derived()
    [3] derived::foo3()
    

    bei Aufruf:

    base *bp = new derived;
    

    wird ein Zeiger this erzeugt, was auf table2 zeigt.Durch diesen Zeiger kann ich natürlich auch foo3()aufrufen, etwa this[2]->foo3();

    das ist dem zeiger base aber nicht zu entnehmen. insbesondere ist der name foo3 überhaupt nicht in der basisklasse bekannt. letzlich fehlen hier eher ein paar grundlagen in OO. eine bestimmte implementation dazu zu benutzen, dies zu lernen, ist eher ungünstig.



  • ich verstehe nicht ganz, in manchen Dokumenten steht z.b.

    base *bp = new derived;
    

    bp ist ein Zeiger von Type base, aber auf derived zeigt.

    in asm ist bp ist einfach ein Variable, drin ist eine Adresse, z.B. 0x1000, ab dieser Adress vorwärts (increment) wurde class derived abgelegt. Hier ist "base"ein platzhalter ohne wirkliche Bedeutung.
    ist es nicht so? oder kann jd von der asm Sicht erklären?



  • Mit "bp" wollte man wohl nur sagen, dass die Variable ein 'base'-'pointer' (also ein Pointer auf ein Objekt der Klasse 'base', eben 'bp') ist 😃

    derived ist doch nichts anderes, als eine Erweiterung von base. Man kann ein Objekt von derived wie ein Objekt von base benutzen, aber halt nicht umgekehrt.


Anmelden zum Antworten