If a class has any virtual function, objects of this class need to have a vptr, that is a pointer to the vtable, that is the virtual table from where the address of the correct virtual function can be found. The function called depends on the dynamic type of the object, that it is the most derived class the object is a base subobject of.
Because the derived class inherits virtually from a base class, the location of the base class relative to the derived class is not fixed, it depends on the dynamic type of the object too. With gcc a class with virtual base classes needs a vptr to locate the base classes (even if there is no virtual function).
Also, the base class contains a data member, which is located just after the base class vptr. Base class memory layout is: { vptr, int
}
If a base class needs vptr, a class derived from it will need a vptr too, but often the "first" vptr of a base class subobject is reused (this base class with the reused vptr is called the primary base). However this is not possible in this case, because the derived class needs a vptr not only to determine how to call the virtual function, but also where the virtual base is. The derived class cannot locate its virtual base class without using the vptr; if the virtual base class was used as a primary base, the derived class would need to locate its primary base to read the vptr, and would need to read the vptr to locate its primary base.
So the derived cannot have a primary base, and it introduces its own vptr.
The layout of a base class subobject of type derived
is thus: { vptr, int
} with the vptr pointing to a vtable for derived, containing not only the address of virtual functions, but also the relative location of all its virtual base classes (here just base
), represented as an offset.
The layout of a complete object of type derived
is: { base class subobject of type derived
, base
}
So the minimum possible size of derived
is (2 int
+ 2 vptr) or 4 words on common ptr = int
= word architectures, or 16 bytes in this case. (And Visual C++ makes bigger objects (when virtual base classes are involved), I believe a derived
would have one more pointer.)
So yes, virtual functions have a cost, and virtual inheritance has a cost. The memory cost of virtual inheritance in this case is one more pointer per object.
In designs with many virtual base classes, the memory cost per object might be proportional to the number of virtual base classes, or not; we would need to discuss specific class hierarchies to estimate the cost.
In designs without multiple inheritance or virtual base classes (or even virtual functions), you might have to emulate many things automatically done by the compiler for you, with a bunch of pointers, possibly pointers to functions, possibly offsets... this could get confusing and error prone.