There is this code:
#include <iostream>
class Base
{
int x;
};
class Derived : virtual public Base
{
int y;
};
int main()
{
std::cout << sizeof(Derived) << std::endl; // prints 12
return 0;
}
I have read that when some class is virtually inherited then there is created empty vtable for class Derived, so memory layout is as follows:
Derived::ptr to empty vtable
Derived::y
Base::x
and it is 12 bytes. The question is - what is purpose of this empty vtable if there are not any virtual methods and how is it used?
Derived
needs some way to know where the Base
subobject is. With virtual inheritance, the relative location of the base class is not fixed with respect to the location of the derived class: it may be located anywhere in the full object.
Consider a more typical example involving diamond inheritance.
struct A
{
int a;
};
struct B1 : virtual A
{
int b1;
};
struct B2 : virtual A
{
int b2;
};
struct C : B1, B2
{
int c;
};
Here, both B1
and B2
derive virtually from A
, so in C
, there is exactly one A
subobject. Both B1
and B2
need to know how to find that A
subobject (so that they can access the a
member variable, or other members of A
if we were to define them).
This is what the vtable is used for in this case: both B1
and B2
will have a vtable that contains the offset of the A
subobject.
To demonstrate what a compiler might do to implement the above diamond inheritance example, consider the following class layouts and virtual tables, generated by the Visual C++ 11 Developer Preview.
class A size(4):
+---
0 | a
+---
class B1 size(12):
+---
0 | {vbptr}
4 | b1
+---
+--- (virtual base A)
8 | a
+---
class B2 size(12):
+---
0 | {vbptr}
4 | b2
+---
+--- (virtual base A)
8 | a
+---
class C size(24):
+---
| +--- (base class B1)
0 | | {vbptr}
4 | | b1
| +---
| +--- (base class B2)
8 | | {vbptr}
12 | | b2
| +---
16 | c
+---
+--- (virtual base A)
20 | a
+---
and the following vtables:
B1::$vbtable@:
0 | 0
1 | 8 (B1d(B1+0)A)
B2::$vbtable@:
0 | 0
1 | 8 (B2d(B2+0)A)
C::$vbtable@B1@:
0 | 0
1 | 20 (Cd(B1+0)A)
C::$vbtable@B2@:
0 | 0
1 | 12 (Cd(B2+0)A)
Note that the offsets are relative to the address of the vtable, and note that for the two vtables generated for the B1
and B2
subobjects of C
, the offsets are different.
(Also note that this is entirely an implementation detail--other compilers may implement virtual functions and bases differently. This example demonstrates one way that they are implemented, and they are very commonly implemented this way.)
To implement virtual functions, C++ uses a special form of late binding known as the virtual table. The virtual table is a lookup table of functions used to resolve function calls in a dynamic/late binding manner. The virtual table sometimes goes by other names, such as “vtable”, “virtual function table”, “virtual method table”, or “dispatch table”.
The virtual table is actually quite simple. First, every class that uses virtual functions (or is derived from a class that uses virtual functions) is given it’s own virtual table. This table is simply a static array that the compiler sets up at compile time. A virtual table contains one entry for each virtual function that can be called by objects of the class. Each entry in this table is simply a function pointer that points to the most-derived function accessible by that class.
- Every class that uses virtual functions (or is derived from a class that uses virtual functions) is given it's own virtual table as a secret data member.
- This table is set up by the compiler at compile time.
- A virtual table contains one entry as a function pointer for each virtual function that can be called by objects of the class.
- Virtual table stores NULL pointer to pure virtual functions.
Virtual Table is created even for classes that have virtual base classes. In this case, the vtable has pointer to the shared instance of the base class along with the pointers to the classe's virtual functions if any.
If you do dynamic_cast<Derived*>(ptr_to_obj), it will use the vtable pointer to determine whether ptr_to_obj refers to a Derived or not. Every class which is involved with virtual methods or inheritance needs a vtable, and it needs to be distinct for each class to support dynamic_cast<>. Even if it doesn't contain any pointers to methods, it is still used to identify the type of the object.
来源:https://stackoverflow.com/questions/9048053/virtual-inheritance-and-empty-vtable-in-base-class