In C++, accessing an unconstructed object is undefined. To avoid such undefined behavior, the object points to different virtual tables (vtable) during construction. If there are Base
and Derived
class, then the objects initially points to the vtable Base
. Later when Derived
starts construction, then the vtable points to `Derived. This answer explains it at the end together with the answer for question 2.
The same rule applies to virtual inheritance. However, in case of virtual inheritance the order of construction is different than with regular inheritance, and the vtable follows that construction order.
In your case you have the lines:
B::B(V* v, A* a)
and
D() : B((A*)this, this) // in class D
This means that before D
is constructed, it constructs its parent B
. B::B
gets this
casted to A*
and D*
. At the time of B::B
, the constructor of D
did not start, so the vtable does not point to D
's methods. The object points to an earlier vtable. The question which vtable the objects uses depends on the order of construction of the parts.
The virtual bases are constructed first, which is only V
in this case. The rest is as usual. Which means that the order is V->A->B->D
.
Here is a list of the different function calls in the code:
f()
- D
has not been constructed yet, so D::f()
can't be called, and the vtable does not point to it. A
is not a base of B
so A::f()
won't be called. The only option left is V::f()
. Note that given a pointer to object, virtual function of siblings are never called. Only the most derived method of the object, its parents, and its children (all the way up to the dynamic type of the object).
g()
- D
has not been constructed yet, so D::g()
can't be called. Since this is a constructor of B
, it can access all its methods, so B::g()
is called.
v->g()
- v
is of type V
and might invoke g()
of one of the subclasses through the virtual method mechanism. D
has not been constructed yet, so D::g()
is not yet in the vtable . Since this is a constructor of B
, the vtable has been updated to point to B
's methods and all the parts that have already been constructed (A
and V
). So, the vtable points to B::g()
.
a->f()
- a
is of type A
, so it might call methods of its parent, but not of its subclass D
because it has not been constructed yet. This means V::f()
or A::f()
. Since the virtual method invocation prefers the most derived method, then A::f()
should be invoked.
I have answered the first original question of:
Why does v->g()
calls B::g()
?
and more.
For the second question, about what the following means:
the virtual call to A::f will be attempted using B's virtual member function table, since that's what is active during B's construction.
The above text talks about the conceptual model of virtual function calls. A virtual function call is as-if through an array of pointers to methods, which is called a "virtual table", the vtable. For every virtual function, such as f()
in your example, the compiled code fetches the pointer-to-method from this vtable. This is very cheap since it is a simple index access into an array. Objects of the derived class have a different vtable, where some method pointers are different.
Code that gets a pointer to parent, does not care if it is the parent or the child. It simply gets the method-pointer from a given index in the vtable at O(1), regardless of the true type of the object. Changing the type in the constructor, from parent class to child class, happens by simple constant-time reassignment of a pointer to the vtable. The quoted text refers to the sequence of events that makes the pointer point to different vtables.
With virtual inheritance there can be more than one vtable active at a given time. The vtable is selected depending on the type through which the method call is performed (e.g. through B
, A
, or V
).
The wording of the text talks about call to A::f
using B's vtable, makes no sense. Even the example code says that in B::B
's calling to f()
invokes V::f()
not A::f()
. I think that the text should have been V::f()
is performed though B's vtable, which is in line with what I wrote.