How many vptrs are usually needed for a object whose clas( child ) has single inheritance with a base class which multiple inherits base1 and base2. What is the strategy for
Why do you care? The simple answer is enough, but I guess you want something more complete.
This is not part of the standard, so any implementation is free to do as they wish, but a general rule of thumb is that in an implementation that uses virtual table pointers, as a zeroth approximation, for the dynamic dispatch you need at most as many pointers to virtual tables as there are classes that add a new virtual method to the hierarchy. (In some cases the virtual table can be extended, and the base and derived types share a single vptr
)
// some examples:
struct a { void foo(); }; // no need for virtual table
struct b : a { virtual foo1(); }; // need vtable, and vptr
struct c : b { void bar(); }; // no extra virtual table, 1 vptr (b) suffices
struct d : b { virtual bar(); }; // extra vtable, need b.vptr and d.vptr
struct e : d, b {}; // 3 vptr, 2 for the d subobject and one for
// the additional b
struct f : virtual b {};
struct g : virtual b {};
struct h : f, g {}; // single vptr, only b needs vtable and
// there is a single b
Basically each subobject of a type that requires its own dynamic dispatch (cannot directly reuse the parents) would need its own virtual table and vptr.
In reality compilers merge different vtables into a single vtable. When d
adds a new virtual function over the set of functions in b
, the compiler will merge the potential two tables into a single one by appending the new slots to the end of the vtable, so the vtable for d
will be a extended version of the vtable for b
with extra elements at the end maintaining binary compatibility (i.e. the d
vtable can be interpreted as a b
vtable to access the methods available in b
), and the d
object will have a single vptr
.
In the case of multiple inheritance things become a bit more complicated as each base needs to have the same layout as a subobject of the complete object than if it was a separate object, so there will be extra vptrs pointing to different regions in the complete object's vtable.
Finally in the case of virtual inheritance things become even more complicated, and there might be multiple vtables for the same complete object with the vptr's being updated as construction/destruction evolves (vptr's are always updated as construction/destruction evolves, but without virtual inheritance the vptr will point to the base's vtables, while in the case of virtual inheritance there will be multiple vtables for the same type)
Anything regarding vptr/vtable is not specified, so this is going to be compiler dependent for the fine details, but the simple cases are handled the same by almost every modern compiler (I write "almost" just in case).
You have been warned.
If you inherit from base classes, and they have a vptr, you naturally have as many inherited vptr in your class.
The question is: When will the compiler add a vptr to a class that already has an inherited vptr?
The compiler will try to avoid adding redundant vptr:
struct B {
virtual ~B();
};
struct D : B {
virtual void foo();
};
Here B
has a vptr, so D
does not get its own vptr, it reuses the existing vptr; the vtable of B
is extended with an entry for foo()
. The vtable for D
is "derived" from the vtable for B
, pseudo-code:
struct B_vtable {
typeinfo *info; // for typeid, dynamic_cast
void (*destructor)(B*);
};
struct D_vtable : B_vtable {
void (*foo)(D*);
};
The fine print, again: this is a simplification of a real vtable, to get the idea.
For non virtual single inheritance, there is almost no room for variation between implementations. For virtual inheritance, there are a lot more variations between compilers.
struct B2 : virtual A {
};
There is a conversion from B2*
to A*
, so a B2
object must provide this functionality:
A*
memberoffset_of_A_from_B2
offset_of_A_from_B2
in the vtableIn general, a class will not reuse the vptr of its virtual base class (but it can in a very special case).