Virtual tables and virtual pointers for multiple virtual inheritance and type casting
The following is true for GCC (and it seems true for LLVM link), but may also be true for the compiler you're using. All these is implementation-dependent, and is not governed by C++ standard. However, GCC write its own binary standard document, Itanium ABI.
I tried to explain basic concepts of how virtual tables are laid out in more simple words as a part of my article about virtual function performance in C++, which you may find useful. Here are answers to your questions:
A more correct way to depict internal representation of the object is:
| vptr | ======= | ======= | <-- your object |----A----| | |---------B---------|
B
contains its base classA
, it just adds a couple of his own members after its end.Casting from
B*
toA*
indeed does nothing, it returns the same pointer, andvptr
remains the same. But, in a nutshell, virtual functions are not always called via vtable. Sometimes they're called just like the other functions.Here's more detailed explanation. You should distinguish two ways of calling member function:
A a, *aptr; a.func(); // the call to A::func() is precompiled! aptr->A::func(); // ditto aptr->func(); // calls virtual function through vtable. // It may be a call to A::func() or B::func().
The thing is that it's known at compile time how the function will be called: via vtable or just will be a usual call. And the thing is that the type of a casting expression is known at compile time, and therefore the compiler chooses the right function at compile time.
B b, *bptr; static_cast<A>(b)::func(); //calls A::func, because the type // of static_cast<A>(b) is A!
It doesn't even look inside vtable in this case!
Generally, no. A class can have several vtables if it inherits from several bases, each having its own vtable. Such set of virtual tables forms a "virtual table group" (see pt. 3).
Class also needs a set of construction vtables, to correctly distpatch virtual functions when constructing bases of a complex object. You can read further in the standard I linked.
Here's an example. Assume
C
inherits fromA
andB
, each class definingvirtual void func()
, as well asa
,b
orc
virtual function relevant to its name.The
C
will have a vtable group of two vtables. It will share one vtable withA
(the vtable where the own functions of the current class go is called "primary"), and a vtable forB
will be appended:| C::func() | a() | c() || C::func() | b() | |---- vtable for A ----| |---- vtable for B ----| |--- "primary virtual table" --||- "secondary vtable" -| |-------------- virtual table group for C -------------|
The representation of object in memory will look nearly the same way its vtable looks like. Just add a
vptr
before every vtable in a group, and you'll have a rough estimate how the data are laid out inside the object. You may read about it in the relevant section of the GCC binary standard.Virtual bases (some of them) are laid out at the end of vtable group. This is done because each class should have only one virtual base, and if they were mingled with "usual" vtables, then compiler couldn't re-use parts of constructed vtables to making those of derived classes. This would lead to computing unnecessary offsets and would decrease performance.
Due to such a placement, virtual bases also introduce into their vtables additional elements:
vcall
offset (to get address of a final overrider when jumping from the pointer to a virtual base inside a complete object to the beginning of the class that overrides the virtual function) for each virtual function defined there. Also each virtual base addsvbase
offsets, which are inserted into vtable of the derived class; they allow to find where the data of the virtual base begin (it can't be precompiled since the actual address depends on the hierarchy: virtual bases are at the end of object, and the shift from beginning varies depending on how many non-virtual classes the current class inherits.).
Woof, I hope I didn't introduce much unnecessary complexity. In any case, you may refer to the original standard, or to any document of your own compiler.
- That seems correct to me. It's not wrong as if you're using a A pointer, you only need what A provide plus maybe B functions implementations that are available from the A vtable (there can be several vtable, depending on compiler and hierarchy complexity).
- I'd say yes, but it's compiler implementation dependant so you don't really have to know about it.
- and 4. Read farther.
I would recommend reading Multiple Inheritance Considered Useful , it's a long article but it makes things clearer about the subject as it explains in great details how inheritance works in C++ (the figures links don't work but they are available at the bottom of the page).