为什么 virtual inherit 2 类 会增加对象大小?

Why virtual inherit 2 classes raises object size?

我有一个简单的对象,在 64 位 ubuntu1804 下用 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;
}

程序输出:

$ g++ om.cpp -O2 && ./a.out
C1=24
C2=24
C3=24
C4=24
D1=32
D2=32
F =64

我知道 sizeof(B1) 是 16,而 Child1-Child4 添加虚函数(vptr 指向 vtable)会增加额外的指针大小,所以它们的大小是 24,没问题。但是为什么 sizeof Derive1/Derive2 是 32 呢? C++ 对象模型为它添加了一个额外的指针,对吧?但是这个额外的指针实际上做了什么,为什么有必要添加这个额外的 8byte 指针呢?我看这里没有任何必要。

非常感谢。

一个合理的布局:

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)
| --------------------
----------------------

总尺寸:8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 = 64
请注意,如果您的虚函数不那么琐碎 and/or 实际上会覆盖某些内容,则此大小可能会增加以容纳 vtable 指针。 (就目前而言,可以完全优化虚拟功能。)

要理解为什么所有这些指针都是必要的,请考虑以下内容:

Final foo;
Child3 * c3 = &foo;
Child4 * c4 = &foo;
Base2 * b23 = c3;
Base2 * b24 = c4;

如果给你 c4,你会如何将其转换为指向 Base2 的指针?请记住,您不能假设 c4 指向 Final 的一部分;您的解决方案还必须适用于以下内容,并且必须同样适用于 c3.

Child4 c4;
Base2 * b24 = &c4;