Note that in order to gracefully handle multiple inheritance, an object can have more than one VPTR, but in general, each of them is likely to be a simple architecture-specific pointer.
Try running something like this to see how your compiler puts things out:
#include <iostream> using namespace std; struct Base { int B; virtual ~Base() {} }; struct Base2 { int B2; virtual ~Base2() {} }; struct Derived : public Base, public Base2 { int D; }; int main(int argc, char* argv[]) { cout << "Base:" << sizeof (Base) << endl; cout << "Base2:" << sizeof (Base2) << endl; cout << "Derived:" << sizeof (Derived) << endl; Derived *d = new Derived(); cout << d << endl; cout << static_cast<Base*>(d) << endl; cout << &(d->B) << endl; cout << static_cast<Base2*>(d) << endl; cout << &(d->B2) << endl; cout << &(d->D) << endl; delete d; return 0; }
In my 32-bit compiler, this gives 8 bytes for the base classes and 20 bytes for the Derived class (and doubles these values ββwhen compiling for 64 bits):
4 bytes Derived/Base VPTR 4 bytes int B 4 bytes Derived/Base2 VPTR 4 bytes int B2 4 bytes int D
You can see how, looking at the first 8 bytes, you can treat Derived as a base, and how, looking at the second 8 bytes, you can treat it like Base2.
source share