Pergunta

#include "stdafx.h"
#include <iostream>
using namespace std;

class ClassA
{
    protected:
       int width, height;
    public:
       void set_values(int x, int y)
       {
         width = x;
         height = y;
       }
};

class ClassB : virtual public ClassA
{
   //12(int + int + vptr)
};

class ClassC : virtual public ClassA
{
  //12(int + int + vptr)
};

class ClassD : public ClassB, public ClassC
{
};

int main()
{
  ClassA A;
  ClassB B;
  ClassC C;
  ClassD D;
  cout << "size = " << sizeof(A) << endl;
  cout << "size = " << sizeof(B) << endl;
  cout << "size = " << sizeof(C) << endl;
  cout << "size = " << sizeof(D) << endl;
  return 0;
}

the output i got is:

size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16

In the above code why the output is 16 for ClassD. please explain me clearly how this virtual inheritance works.

Foi útil?

Solução

Virtual inheritance means, that the virtual base classes only exist once instead of multiple times. That is why the 8 bytes from ClassA are only in ClassD once. Virtual inheritance itself requires a certain overhead and hence you get an additional pointer. The exact implementation and therefore the exact overhead is not specified by the C++ standard and may vary depending on the hierarchy you are creating.

Outras dicas

When ClassD inherites ClassB and ClassC there will be two vptrs (one from B and one from C). This exact case is described in Scott Meyers' "More Effective C++", Item 24 (The Cost of Various Language Features).

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top