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.
size of derived class in virtual inheritance
-
22-03-2022 - |
質問
#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.
解決
他のヒント
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 ofClassA
base fromClassB
base - a
ClassC
subject points to the ClassC-in-ClassD vtable, which indicates the relative position ofClassA
base fromClassC
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.