Search code examples
c++inheritancepointersvirtualvtable

why Base class and Drive class have same virtual pointer but 2 vtable when initiate a Drive instance


#include <iostream>
using namespace std;

class Base {
public:
    Base() {
        cout << "In Base" << endl;
        cout << "Virtual Pointer = " << (int*)this << endl;
        cout << "Address of Vtable = "
        << (int*)*(int*)this << endl;
        cout << "Value at Vtable = "
        << (int*)*(int*)*(int*)this << endl;
        cout << endl;
    }

    virtual void f1() { cout << "Base::f1" << endl; }
};

class Drive : public Base {
public:
    Drive() {
        cout << "In Drive" << endl;
        cout << "Virtual Pointer = "
        << (int*)this << endl;
        cout << "Address of Vtable = "
        << (int*)*(int*)this << endl;
        cout << "Value at Vtable = "
        << (int*)*(int*)*(int*)this << endl;
        cout << endl;
    }

    virtual void f1() { cout << "Drive::f2" << endl; }
};

int main() {
Drive d;
return 0;

}

The output of this program is

In Base
Virtual Pointer = 0012FF7C
Address of Vtable = 0046C08C
Value at Vtable = 004010F0

In Drive
Virtual Pointer = 0012FF7C
Address of Vtable = 0046C07C
Value at Vtable = 00401217

Follow the code, i can see that when i create a Drive object, the Base constructor also run and show the address of virtual pointer same with Drive's virtual pointer: 0012FF7C. The weird thing is that when i dereference that address in both constructors of Base and Drive class, it pointed to different value meaning there are two vtable, one at 0046C08C and another at 0046C07C. Its very difficult to understand in structure of Drive object and also in c langue when 1 pointer point 2 address.


Solution

  • You've just witnessed how your C++ compiler implements the rules set out by the C++ standard. While the Base constructor is running, any calls to virtual methods need to be dispatched to the Base implementations, even if Drive overrides them. To make that happen, your C++ compiler evidently makes the object's vtable pointer point to the Base vtable. When the Base constructor finishes and execution continues in the Drive constructor, the vtable pointer gets updated to point at the Drive vtable.

    That ends up being a convenient way to implement it. The rest of code, where the compiler generates instructions to call virtual methods, doesn't need to change to detect whether the special constructor behavior is required. It can just look in the vtable like always. Changing the vtable pointer is a quick way to change the effective run-time type of the object while the constructors run.

    You'll probably find the destructors work in a similar manner, but in reverse. If you construct a Base object instead of a Drive object, you'll probably see similar addresses as you do in the Base constructor of your Drive object.