0
votes

I have a class 'base' with a virtual destructor and thus a VTable and corresponding VTPR in it, and a class derived from it:

class base {
public:
    virtual ~base() {}
};

class der : base {};

main()
{

    int a = sizeof(base); // = 4 , fine !

    int b = sizeof(der);  // = 4 too ?
}

Now as derived class too is virtual, it'll have a VPTR of its own, but since it also has a subobject of the base class with a VPTR in it, shouldn't the size of the class 'der' be 8 bytes i.e. the size of the VPTR of the class 'der' + size of VPTR of the subobject of class 'base'? (when sizeof(void*) = 4 bytes ).

So basically my question is : When the subobject of class 'base' is made in 'der' does it have a seperate new VPTR ? And if it is so then why its size is not getting added while calculating the size of 'der'?

Can somebody please clarify this.

2
Duplicating the vptr in the derived classes would completely defeat the purpose of vptrs: when manipulating a der instance with a base pointer, how would the implementation know that it is supposed to fetch the vptr in the derived part of the object? However, it is interesting to note that an object can have multiple vptrs if it inherits from multiple polymorphic base classes: struct A { virtual ~A() {} }; struct B { virtual ~B() {} }; struct C : A, B {};. On some (most?) implementations, C will have two vptrs, one in the A subobject and one in the B subobject. - Luc Touraille
the struct C above will surely have two VPTRs but shouldn't these VPTRs be in the class C itself instead of being in the subobjects (as u stated), because it was not so, then how would the implementation know that it is supposed to fetch the vptr in the derived part of the object? - cirronimbo
@LucTouraille "On some (most?) implementations, C will have two vptrs" This is a very degenerate example as you have zero non special member: no data member and no normal virtual function. It means that the only operations that might need a vtable lookup are the non array delete and explicit dtor call. None of these are even allowed in a c-dtor: they aren't part of the virtual behavior of an object under construction (and they notably aren't part of the construction vtable for a virtual base for a base class ctor). If C was a triangle, not only its area would be zero, its diameter also! - curiousguy
So the issue here is whether both bases could be put at the same (zero) offset, by sharing for both bases one vptr field, by sharing a vptr that is a vtable address, by sharing the vtable, by putting both bases at the same (zero) offset. This can work by total agreement in the runtime behavior of pointers to both bases. Vtables support virtual calls, conversions to virtual bases (none), conversion to most derived class, lookup for a derived class or base of derived class (dynamic_cast<T*>) and type identification (typeid); it's seems OK. Note: base class dtors need to set the vptr. - curiousguy
Note that the fact that the vptr is value is the same is used indirectly several times in the justification of the fact the vptr can be the same. That is made possible by the identical offset of both bases in any more derived class (made possible by the sharing) so the this adjustment in a derived class overrider (which might be non zero in further derived) is the same. The offset from the most derived class is obviously the same and so are the offsets for casting to a base of that that derived type via dynamic_cast<T*>. - curiousguy

2 Answers

4
votes

This is all implementation-specific. But in practice, there will only be one vptr in the derived class; there is no need for two. The whole point of the vptr is it's what gets used to dynamically call the correct override of the virtual function; der objects will simply have a different pointer value to base objects.

[Note: Your example is probably confused by the fact that you are (unintentionally?) using private inheritance, rather than the more typical public inheritance...]

2
votes

I think you're confusing vtables and vptrs. Each class will have a vtable, and each object will store a pointer to its vtable as the vptr. The vtable is like a static global, it is shared between all instances of the class and thus doesn't take any space in the object.