0

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.

cirronimbo
  • 909
  • 9
  • 19
  • 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 Jun 05 '12 at 15:06
  • 1
    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 Jun 05 '12 at 15:39
  • @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 Nov 20 '19 at 21:52
  • 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`) and type identification (`typeid`); it's seems OK. Note: base class dtors need to set the vptr. – curiousguy Nov 20 '19 at 22:16
  • 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`. – curiousguy Nov 20 '19 at 22:25
  • IOW that class `C` has **two primary direct bases.** Note that this is almost always impossible for unrelated types (this is obviously different when the two bases are `A` and `B` derived virtually from `A`). Any data member, even empty (as long as the member has an identity), would make that impossible, and virtual functions will likely make that impossible, that is unless they have the same function overrider, that is they have the same signature: same name, parameter list, qualification. (A dtor is just "the destructor", it has no written name.) – curiousguy Nov 20 '19 at 22:28

2 Answers2

4

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...]

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • I didn't use public inheritance because (as far as I know) it made no difference to the thing I'm asking. And sorry, I couldn't get your answer. What I was asking is about VPTR in the sub object of 'base'. – cirronimbo Jun 05 '12 at 15:00
  • cirronimmplo: Wwhat he's saying is that the derived class has no need of the virtual ptr to the base class, and so the vptr for the derived class overwrites it. – Mooing Duck Jun 05 '12 at 15:07
  • @cirronimbo: If you have a `base` object, then the vptr points at the vtable for `base`. If you have a `der` object, then the vptr points at the vtable for `der`. – Oliver Charlesworth Jun 05 '12 at 15:12
  • Ok. Got it now. Thanks :) But what happens in case of multiple (non-virtual) inheritance, inheriting from two base classes. Does it lead to formation of two VPTRs in the derived class ? – cirronimbo Jun 05 '12 at 15:24
  • 1
    @cirronimbo: Multiple inheritance in C++ has all sorts of intricacies (especially when you get inheritance diamonds). But in a nutshell, yes, there is one vptr per inheritance path. – Oliver Charlesworth Jun 05 '12 at 15:26
  • "_Your example is probably confused by the fact that you are (unintentionally?) using private inheritance_" Do you imply that it would change the class memory layout? – curiousguy Nov 20 '19 at 22:29
2

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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622