问题
I read this question: C++ Virtual class inheritance object size issue, and was wondering why virtual inheritance results in an additional vtable pointer in the class.
I found an article here: https://en.wikipedia.org/wiki/Virtual_inheritance
which tells us:
However this offset can in the general case only be known at runtime,...
I don't get what is runtime-related here. The complete class inheritance hierarchy is already known at compile time. I understand virtual functions and the use of a base pointer, but there is no such thing with virtual inheritance.
Can someone explain why some compilers (Clang/GCC) implement virtual inheritance with a vtable and how this is used during runtime?
BTW, I also saw this question: vtable in case of virtual inheritance, but it only points to answers related to virtual functions, which is not my question.
回答1:
The complete class inheritance hierarchy is already known in compile time.
True enough; so if the compiler knows the type of a most derived object, then it knows the offset of every subobject within that object. For such a purpose, a vtable is not needed.
For example, if B and C both virtually derive from A, and D derives from both B and C, then in the following code:
D d;
A* a = &d;
the conversion from D* to A* is, at most, adding a static offset to the address.
However, now consider this situation:
A* f(B* b) { return b; }
A* g(C* c) { return c; }
Here, f must be able to accept a pointer to any B object, including a B object that may be a subobject of a D object or of some other most derived class object. When compiling f, the compiler doesn't know the full set of derived classes of B.
If the B object is a most derived object, then the A subobject will be located at a certain offset. But what if the B object is part of a D object? The D object only contains one A object and it can't be located at its usual offsets from both the B and C subobjects. So the compiler has to pick a location for the A subobject of D, and then it has to provide a mechanism so that some code with a B* or C* can find out where the A subobject is. This depends solely on the inheritance hierarchy of the most derived type---so a vptr/vtable is an appropriate mechanism.
回答2:
However this offset can in the general case only be known at runtime,...
I can't get the point, what is runtime related here. The complete class inheritance hierarchy is already known in compile time.
The linked article at Wikipedia provides a good explanation with examples, I think.
The example code from that article:
struct Animal {
virtual ~Animal() = default;
virtual void Eat() {}
};
// Two classes virtually inheriting Animal:
struct Mammal : virtual Animal {
virtual void Breathe() {}
};
struct WingedAnimal : virtual Animal {
virtual void Flap() {}
};
// A bat is still a winged mammal
struct Bat : Mammal, WingedAnimal {
};
When you careate an object of type Bat, there are various ways a compiler may choose the object layout.
Option 1
+--------------+
| Animal |
+--------------+
| vpointer |
| Mammal |
+--------------+
| vpointer |
| WingedAnimal |
+--------------+
| vpointer |
| Bat |
+--------------+
Option 2
+--------------+
| vpointer |
| Mammal |
+--------------+
| vpointer |
| WingedAnimal |
+--------------+
| vpointer |
| Bat |
+--------------+
| Animal |
+--------------+
The values contained in vpointer in Mammal and WingedAnimal define the offsets to the Animal sub-object. Those values cannot be known until run time because the constructor of Mammal cannot know whether the subject is Bat or some other object. If the sub-object is Monkey, it won't derive from WingedAnimal. It will be just
struct Monkey : Mammal {
};
in which case, the object layout could be:
+--------------+
| vpointer |
| Mammal |
+--------------+
| vpointer |
| Monkey |
+--------------+
| Animal |
+--------------+
As can be seen, the offset from the Mammal sub-object to the Animal sub-object is defined by the classes derived from Mammal. Hence, it can be defined only at runtime.
来源:https://stackoverflow.com/questions/57482925/why-does-virtual-inheritance-need-a-vtable-even-if-no-virtual-functions-are-invo