I just read about this in the C++ FAQ Lite
[25.10] What does it mean to “delegate to a sister class” via virtual inheritance?
class Base {
public:
virtual void foo() = 0;
virtual void bar() = 0;
};
class Der1 : public virtual Base {
public:
virtual void foo();
};
void Der1::foo()
{ bar(); }
class Der2 : public virtual Base {
public:
virtual void bar();
};
class Join : public Der1, public Der2 {
public:
...
};
int main()
{
Join* p1 = new Join();
Der1* p2 = p1;
Base* p3 = p1;
p1->foo();
p2->foo();
p3->foo();
}
“Believe it or not, when Der1::foo() calls this->bar(), it ends up calling Der2::bar(). Yes, that’s right: a class that Der1 knows nothing about will supply the override of a virtual function invoked by Der1::foo(). This “cross delegation” can be a powerful technique for customizing the behavior of polymorphic classes. “
My question is:
-
What is happening behind the scene.
-
If I add a Der3 (virtual inherited from Base), what will happen? (I dont have a compiler here, couldn’t test it right now.)
The simple explanation is that, because inheritance from
Baseis virtual in bothDer1andDer2, there is a single instance of the object in the most derived objectJoin. At compile time, and assuming (which is the common case) virtual tables as dispatch mechanism, when compilingDer1::fooit will redirect the call tobar()through the vtable.Now the question is how the compiler generates vtables for each of the objects, the vtable for
Basewill contain two null pointers, the vtable forDer1will containDer1::fooand a null pointer and the vtable forDer2will contain a null pointer andDer2::bar[*]Now, because of virtual inheritance in the previous level, when the compiler processes
Joinit will create a singleBaseobject, and thus a single vtable for theBasesubojbect ofJoin. It effectively merges the vtables ofDer1andDer2and produces a vtable that contains pointers toDer1::fooandDer2::bar.So the code in
Der1::foowill dispatch throughJoin‘s vtable to the final overrider, which in this case is in a different branch of the virtual inheritance hierarchy.If you add a
Der3class, and that class defines either of the virtual functions, the compiler will not be able to cleanly merge the three vtables and will complain, with some error relating to the ambiguity of the multiply defined method (none of the overriders can be considered to be the final overrider). If you add the same method toJoin, then the ambiguity will no longer be a problem, as the final overrider will be the member function defined inJoin, so the compiler is able to generate the virtual table.[*] Most compilers will not write null pointers here, but rather a pointer to a generic function that will print an error message and
terminatethe application, allowing for better diagnostics than a plain segmentation fault.