I was reading the Wikipedia article on virtual inheritance. I followed the whole article but I could not really follow the last paragraph
This is implemented by providing
Mammal and WingedAnimal with a vtable
pointer (or "vpointer") since, e.g.,
the memory offset between the
beginning of a Mammal and of its
Animal part is unknown until runtime.
Thus Bat becomes
(vpointer,Mammal,vpointer,WingedAnimal,Bat,Animal).
There are two vtable pointers, one per
inheritance hierarchy that virtually
inherits Animal. In this example, one
for Mammal and one for WingedAnimal.
The object size has therefore
increased by two pointers, but now
there is only one Animal and no
ambiguity. All objects of type Bat
will have the same vpointers, but each
Bat object will contain its own unique
Animal object. If another class
inherits from Mammal, such as
Squirrel, then the vpointer in the
Mammal object in a Squirrel will be
different from the vpointer in the
Mammal object in a Bat, although they
can still be essentially the same in
the special case that the Squirrel
part of the object has the same size
as the Bat part, because then the
distance from the Mammal to the Animal
part is the same. The vtables are not
really the same, but all essential
information in them (the distance) is.
Can someone please shed some more light on this.
Sometimes, you just really need to see some code / diagrams :) Note that there is no mention of this implementation detail in the Standard.
First of all, let's see how to implement methods in C++:
struct Base
{
void foo();
};
This is similar to:
struct Base {};
void Base_foo(Base& b);
And in fact, when you look at a method call within a debugger, you'll often see the this
argument as the first parameter. It is sometimes called an implicit parameter.
Now, on to the virtual table. In C and C++ it is possible to have pointers to function. A vtable is essentially a table of pointers to functions:
struct Base
{
int a;
};
void Base_set(Base& b, int i) { b.a = i; }
int Base_get(Base const& b) { return b.a; }
struct BaseVTable
{
typedef void (*setter_t)(Base&, int);
typedef int (*getter_t)(Base const&);
setter_t mSetter;
getter_t mGetter;
BaseVTable(setter_t s, getter_t g): mSetter(s), mGetter(g) {}
} gBaseVTable(&Base_set, &Base_get);
Now I can do something like:
void func()
{
Base b;
(*gBaseVTable.mSetter)(b, 3);
std::cout << (*gBaseVTable.mGetter)(b) << std::endl; // print 3
}
Now, on to the inheritance. Let's create another structure
struct Derived: Base {}; // yeah, Base does not have a virtual destructor... shh
void Derived_set(Derived& d, int i) { d.a = i+1; }
struct DerivedBaseVTable
{
typedef void (*setter_t)(Derived&,int);
typedef BaseVTable::getter_t getter_t;
setter_t mSetter;
getter_t mGetter;
DerivedBaseVTable(setter_t s, getter_t g): mSetter(s), mGetter(g) {}
} gDerivedBaseVTable(&Derived_set, &Base_get);
And the use:
void func()
{
Derived d;
(*gDerivedBaseVTable.mSetter)(d, 3);
std::cout << (*gDerivedBaseVTable.mGetter)(d) << std::endl; // print 4
}
But how to automate this ?
- you only need one instance of a vtable per class having at least one virtual function
- each instance of the class will contain a pointer to the vtable as its first attribute (even though you can't really access it by yourself)
Now, what happens in case of multi-inheritance ? Well, inheritance is very much like composition in term of memory layout:
| Derived |
| BaseA | BaseB |
| vpointer | field1 | field2 | padding? | vpointer | field1 | field2 | padding? |
There will thus be 2 virtual tables for MostDerived
: one to change the methods from BaseA
and one to change the methods from BaseB
.
Pure virtual functions are generally represented as a null pointer (simply) in the corresponding field.
And finally, construction and destruction:
Construction
BaseA
is constructed: first the vpointer is initialized, then the attributes, then the body of the constructor is executed
BaseB
is constructed: vpointer, attributes, body
Derived
is constructed: replace the vpointers (both), attributes, body
Destruction
Derived is destructed
: body of the destructor, destroy attributes, put the base vpointers back
BaseB
is destructed: body, attributes
BaseA
is destructed: body, attributes
I think it's pretty comprehensive, I'd be glad if some C++ gurus around there could review this and check I haven't made any stupid mistake. Also, if something is missing, I'd be glad to add it.
I cannot, really. This section tries to describe what should be done in a C++ implementation using virtual method tables to provide dynamic binding (in case of multiple inheritance).
If you're not doing a compiler, my advice is: Don't bother. Read your favorite C++ book on inheritance, virtual methods, mulitple inheritance and virtual inheritance.
Plus, usage of a vtable is not required by the C++ standard (IIRC), it's an implementation detail. So really, don't bother.
As mkluwe suggested, vpointers are not really a part of the language. However, knowing about
implementation techniques might be useful, especially in a low-level language like C++.
If you really want to learn this, I would recommend Inside the C++ Object Model, which explains this and a lot of other things in detail.