Virtual base classes implementations
Virtual base classes are exactly like virtual function: their address (or relative address aka offset) is not known at compile time:
void f(ClassB *pb) {
ClassA *pa = pb;
}
Here the compiler must compute the offset of the ClassA
base subobject from the ClassB
subobject (or mostly derived object). Some compiler simply have a pointer to it inside ClassB
; others use the vtable, just like for virtual functions.
In both case the overhead in ClassB
is one pointer.
The ClassC
is similar, but the vptr will be point to a ClassC
vtable, not a ClassB
vtable.
Thus a ClassD
object will contain (this is not an ordered list):
- a single
ClassA
subobject
- a
ClassB
subject
- a
ClassC
subject
So ClassD
has two inherited vptr: from ClassB
and ClassC
. In a ClassD
object, both vptr will point to some ClassD
vtable, but the same ClassD
vtable:
- a
ClassB
subject points to the ClassB-in-ClassD vtable, which indicates the relative position of ClassA
base from ClassB
base
- a
ClassC
subject points to the ClassC-in-ClassD vtable, which indicates the relative position of ClassA
base from ClassC
base
Possible optimization
I guess your question is: do we need two distinct vptr?
Technically, it is sometimes possible to optimize the size of classes by overlaying base class subobjects. This is one of those cases where it is technically possible:
Overlaying (or unification) means that both ClassB
and ClassC
will share the same vptr: given d
an instance of ClassD
:
&d.ClassB::vptr == &d.ClassC::vptr
so d.ClassB::vptr == d.ClassC::vptr
but d.ClassB::vptr == &ClassC_in_ClassD_vtable
and d.ClassC::vptr == &ClassC_in_ClassD_vtable
, so ClassB_in_ClassD_vtable
must be unified with ClassC_in_ClassD_vtable
. In this particular case, both ClassB_in_ClassD_vtable
and ClassC_in_ClassD_vtable
are only used to describe the offset of ClassA
subobject; if ClassB
and ClassC
subobjects are unified in ClassD
, then these offsets are unified too, hence the unification of the vtables are possible.
Note that this is only possible here as there is perfect similarity. If ClassB
and ClassC
were modified to add even one virtual function in each, such as these virtual functions are not equivalent (hence not unifiable), the vtable unification would not be possible.
Conclusion
This optimization is only possible in very simple cases like this one. These cases are not typical of C++ programming: people normally use virtual base classes in combination with virtual functions. Empty base class optimization is useful because many C++ idioms use base classes with no data members or virtual functions. OTOH, a tiny (one vptr) space optimization for a special use of virtual base classes does not seem useful for real world programs.