【问题标题】:Why virtual inherit 2 classes raises object size?为什么虚拟继承 2 个类会增加对象大小?
【发布时间】:2019-10-11 18:07:51
【问题描述】:

我有一个简单的对象,用g++在64位ubuntu1804下编译运行:

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,没问题。但是为什么 Derive1/Derive2 的 sizeof 是 32? c++ 对象模型为它添加了一个额外的指针,对吧?但是这个额外的指针实际上做了什么,为什么有必要添加这个额外的 8 字节指针?我认为这里没有任何必要性。

非常感谢。

【问题讨论】:

  • 我认为它添加了两个指向虚拟继承的Base1Base2 子对象的指针。
  • 这取决于 abi:例如 Itanium C++ abi。根据这个 abi,​​类型的大小与您观察到的相同。 D1 布局为D1{ C1{vtable_ptr:8bytes};C2{vtable_ptr:8bytes};Base1:16bytes}}
  • 想一想——您可以将派生对象的指针强制转换为任何基类,并且在第一个字节处应该有一个有效的 vtable 指针。因此,在多重继承的情况下,每个基类都有一个 vtable 是很合乎逻辑的

标签: c++ object inheritance model virtual


【解决方案1】:

合理的布局:

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
请注意,如果您的虚函数不那么琐碎和/或实际上覆盖了某些东西,则此大小可能会增长以容纳 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;

【讨论】:

    猜你喜欢
    • 2018-11-12
    • 2014-04-30
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多