2016-03-18 21 views
2

Dies ist ähnlich wie bei vielen vorherigen Fragen, aber es fragt etwas, was ich nicht finden konnte Antwort.vptr bei mehrfacher Vererbung auswählen

#include <iostream> 
using namespace std; 

class Base1 { 
    public: 
     int b1_data; 
     virtual void b1_fn() {cout << "I am b1\n";} 
}; 
class Base2 { 
    public: 
     int b2_data; 
     virtual void b2_fn() {cout << "I am b2\n";} 
}; 
class Derived : public Base1, public Base2 { 
    public: 
     int d_data; 
     void b1_fn() {cout << "I am b1 of d\n";} 
     void b2_fn() {cout << "I am b2 of d\n";} 
}; 

int main() { 
    Derived *d = new Derived(); 
    Base1 *b1 = d; 
    /*My observation mentioned below is implementation dependant, for learning, 
    I assume, there is vtable for each class containing virtual function and in 
    case of multiple inheritance, there are multiple vtables based on number of 
    base classes(hence that many vptr in derived object mem layout)*/ 

    b1->b1_fn(); // invokes b1_fn of Derived because d points to 
       // start of d's memory layout and hence finds vtpr to 
       // Derived vtable for Base1(this is understood) 
    Base2 *b2 = d; 
    b2->b2_fn(); // invokes b2_fn of Derived but how? I know that it "somehow" 
       // gets the offset added to d to point to corresponding Base2 
       // type layout(which has vptr pointing to Derived vtable for 
       // Base2) present in d's memory layout. 
    return 0; 
} 

Insbesondere wie kommt b2 Punkt für VTable vptr von für Base2 abgeleitet b2_fn get()? Ich habe versucht, Memlayout Dump von GCC zu sehen, konnte aber nicht viel herausfinden.

+2

Ihre Frage ist also: "Wie implementiert GCC virtuelle Funktionen mit Mehrfachvererbung"? –

+0

Art von ja, aber um es einzugrenzen, möchte ich wissen, wie es die Bindung durchführt, wenn es mehrere Basisklassen gibt und alle nicht im abgeleiteten Objekt-Layout bei gleichem Offset zugeordnet sind. Ich suche im Grunde die Quelle dieses "Offset". –

Antwort

2

Der Compiler im Falle der Mehrfachvererbung, konstruieren seine Vtables, so dass jedes Unterobjekt eine entsprechende Vtable hat. Dies ist natürlich abhängig von der Implementierung (wie die vtables selbst), aber es so organisiert werden würde:

  • A Base1 Objekt eine vptr auf eine Vtable zeigt einen eindeutigen Zeiger auf Base1::b1_fn
  • A Base2 Objekt enthält ein vptr auf eine vtable zeigt einen einzigartigen Zeiger enthält, auf Base2::b2_fn
  • ein Derived Objekt eine vptr auf eine vtable, die zeigen entsprechendmit der vtable Layout beginnt, erweitert es aber um die fehlenden Elemente der vtable von Base2. Mit "Layout" meine ich, dass der Zeiger für b1_fn() auf dem gleichen Offset ist, aber es könnte auf eine übergeordnete Funktion hinweisen. Also, hier würde die Tabelle Derived::b1_fn gefolgt von Derived::b2_fn enthalten. Dieses kombinierte Layout stellt sicher, dass das Unterobjekt Base1 in Derived den vtable mit seinem Kind teilen kann.
  • Aber ein Derived Objekt besteht aus zwei Teilobjekten zusammengesetzt: so die Base1 Subobjekt wird von einem der Base2 subobject gefolgt werden, die ihre eigenen VTable haben wird, erforderlich, um das Layout mit für Base2, aber wieder mit Base2::b2_fn anstelle des Originals ein.

Wenn die Derived Zeiger auf einen Zeiger Base2 Gießen, der Compiler wird es das Base2 Subobjekt mit vtable macht Punkt, durch Anlegen einer zum Zeitpunkt der Kompilierung Offset bestimmt fixiert.

Übrigens, würde der Compiler in ähnlicher Weise den festen Offset in die andere Richtung verwenden, um den Anfang des Derived zu finden. Das ist alles ziemlich einfach, bis Sie virtuelle Basen verwenden, in denen die Technik des festen Offsets nicht mehr funktionieren kann. Virtuelle Basiszeiger müssen dann verwendet werden, wie in dieser other SO answer

Diese Dr.Dobb's article ist die beste Erklärung für alle diese Layouts mit einigen guten Bildern.

+0

"_Virtuelle Basiszeiger müssen dann verwendet werden_" Dies ist nur eine Implementierung, keine Notwendigkeit – curiousguy

+0

@curiousguy ja natürlich! Virtuelle Basiszeiger und vptr sind beide Implementierungsmechanismen, die zum Implementieren des C++ - Standards verwendet werden und gehören nicht selbst zum Standard. So könnte jede Alternative mit denselben Effekten verwendet werden. Haben Sie, um der Neugierde willen, etwas Bestimmtes? – Christophe

+0

Es gibt keine brauchbare Alternative zu vptr, aber virtuelle Basiszeiger haben eine offensichtliche Alternative: keine virtuellen Basiszeiger.Der Versatz der virtuellen Basis ist in der VTable. GCC-Regeln. – curiousguy