I've got a simple object, compile and run under 64bit ubuntu1804 with g++:
struct Base1{ int mi,mj,mk,mh;};
struct Base2{ int ni,nj,nk,nh;};
struct Child1:virtual Base1{virtual void f(){}};
struct Child2:virtual Base1{virtual void f(){}};
struct Derive1:Child1,Child2{};
struct Child3:virtual Base2{virtual void f(){}};
struct Child4:virtual Base2{virtual void f(){}};
struct Derive2:Child3,Child4{};
struct Final:Derive1,Derive2{};
int main(){
cout<<"C1="<<sizeof(Child1)<<endl;
cout<<"C2="<<sizeof(Child2)<<endl;
cout<<"C3="<<sizeof(Child3)<<endl;
cout<<"C4="<<sizeof(Child4)<<endl;
cout<<"D1="<<sizeof(Derive1)<<endl;
cout<<"D2="<<sizeof(Derive2)<<endl;
cout<<"F ="<<sizeof(Final)<<endl;
return 0;
}
The program outputs:
$ g++ om.cpp -O2 && ./a.out
C1=24
C2=24
C3=24
C4=24
D1=32
D2=32
F =64
I know that sizeof(B1) is 16, and Child1-Child4 as adding virtual function(vptr pointing to vtable) will add an extra pointer size, so they're of size 24, no problem. But why sizeof Derive1/Derive2 are 32? The c++ object model adds an extra pointer to it, right? But what doesn this extra pointer actually do, and why it's necessary to add this extra 8byte pointer? I don't see any necessity here.
Thanks a lot.
A plausible layout:
Final
----------------------
| Derive1
| --------------------
| | Child1
| | ------------------
| | | Pointer to Base1 (8 bytes)
| | ------------------
| | Child2
| | ------------------
| | | Pointer to Base1 (8 bytes)
| | ------------------
| --------------------
| Derive2
| --------------------
| | Child3
| | ------------------
| | | Pointer to Base2 (8 bytes)
| | ------------------
| | Child4
| | ------------------
| | | Pointer to Base2 (8 bytes)
| | ------------------
| --------------------
| Base1
| --------------------
| | mi (4 bytes)
| | mj (4 bytes)
| | mk (4 bytes)
| | mh (4 bytes)
| --------------------
| Base2
| --------------------
| | ni (4 bytes)
| | nj (4 bytes)
| | nk (4 bytes)
| | nh (4 bytes)
| --------------------
----------------------
Total size: 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 = 64
Note that this size could grow to accommodate vtable pointers if your virtual functions were less trivial and/or actually overrode something. (As it stands, the virtual functions can be optimized completely away.)
To understand why all these pointers are necessary, consider the following:
Final foo;
Child3 * c3 = &foo;
Child4 * c4 = &foo;
Base2 * b23 = c3;
Base2 * b24 = c4;
If you were given c4
, how would you convert it to a pointer to Base2
? Keep in mind that you are not allowed to assume that c4
points to part of Final
; your solution must also work for the following, and it must be equally applicable to c3
.
Child4 c4;
Base2 * b24 = &c4;