C++ 虚拟继承内存布局

Posted

技术标签:

【中文标题】C++ 虚拟继承内存布局【英文标题】:C++ Virtual Inheritance Memory Layout 【发布时间】:2015-09-01 10:04:57 【问题描述】:

虚拟继承内存布局

我试图通过虚拟继承和 vTables/vPtrs 完全了解内存中发生的事情以及其他情况。

我有两个我编写的代码示例,我完全理解它们为什么起作用,但是我只是想确保我对对象内存布局有正确的想法。

Here是图中的两个例子,我只是想知道我对所涉及的内存布局的想法是否正确。

示例 1:

class Top  public: int a;  ;
class Left : public virtual Top   public: int b; ;
class Right : public virtual Top  public: int c; ;
class Bottom : public Left, public Right  public:  int d; ;

示例 2:

同上,但有:

class Right : public virtual Top 
public:
    int c;
    int a;  // <======= added this
;

【问题讨论】:

一个好的起点是the itanium abi,因为这都是特定于实现的 @Mgetz 有点不确定为什么你会建议从 Itanium ABI 开始,当 x86 或 amd64 ABI 在统计上更有可能与原始问题相关 - 或者我可能遗漏了一些东西确定安腾平台的问题...? @twalberg 因为安腾 ABI 甚至在 X86-64 for c++ 上也被用作标准 ABI Virtual tables and memory layout in multiple virtual inheritance的可能重复 您的问题似乎自相矛盾:Top 应该是多态的吗? 【参考方案1】:

C++ 标准对对象布局没有多说。虚拟函数表 (vtables) 和虚拟基指针甚至不是标准的一部分。所以问题和答案只能说明可能的实现。

快速浏览一下您的绘图似乎显示了正确的布局。

您可能会对这些进一步的参考资料感兴趣:

Multiple Inheritance Considered Useful ddj 一篇关于多继承和虚继承情况下布局的文章。

Microsoft patent 描述了 vfptr(虚拟函数表,又名 vtables)和 vbptr(虚拟基础指针)的使用。

编辑:Bottom 是否继承 Right::aLeft::a

在您的测试 2 中,RightLeft 共享同一个共同父级 Top。所以Bottom中只有一个子对象Top,因此,只有一个相同的Top::a

有趣的是,您在测试 2 中在 Right 中引入了成员 a。这是一个a,它不同于继承自Topa。它是一个独特的成员,只是“巧合”地与另一个成员同名。因此,如果您通过Right 访问aRight::a 将隐藏Top::a(顺便说一下,Bottom::Top::aRight::Top::aLeft::Top::a)。在这种情况下,bottom.a 表示 Right::a,不是因为对象布局,而是因为名称查找(和隐藏)规则。而且这不依赖于实现:它是标准且可移植的。

这里有一个变体供您的测试 2 演示情况:

int main() 
    Bottom bottom; 
    bottom.a = 7; 
    cout << bottom.Top::a << endl << bottom.Left::Top::a << endl;
    cout << bottom.Right::Top::a << endl << bottom.Left::a << endl;
    cout << bottom.Right::a << endl <<endl;
    bottom.Right::a = 4; 
    bottom.Left::a = 3; 
    cout << bottom.Top::a << endl << bottom.Left::Top::a << endl;
    cout << bottom.Right::Top::a << endl << bottom.Left::a << endl;
    cout << bottom.Right::a << endl <<endl;

    cout << "And the addresses are: " << endl; 
    cout << " Bottom:       " << (void*)&bottom << endl; 
    cout << " Top:          " << (void*)static_cast<Top*>(&bottom) << endl;
    cout << " Left:         " << (void*)static_cast<Left*>(&bottom) << endl;
    cout << " Right:        " << (void*)static_cast<Right*>(&bottom) << endl;
    cout << " Top::a:       " << (void*)&bottom.Top::a << endl;
    cout << " Left::Top::a: " << (void*)&bottom.Left::Top::a << endl;
    cout << " Right::Top::a:" << (void*)&bottom.Right::Top::a << endl;
    cout << " Left::a:      " << (void*)&bottom.Left::a << endl;
    cout << " Rigth::a:     " << (void*)&bottom.Right::a << endl;
; 

【讨论】:

非常感谢。不过,关于测试 2 中底部的布局的问题 - Bottom 是否仅继承 Right::a,因为它是最近被覆盖的,还是继承了两者,并使用一些逻辑来确定哪一个应该用作 bottom.a?非常感谢 @DomFarolino 我已经编辑了我的答案以澄清这方面。 啊好吧,我知道声明Right::a 会使Bottom.a = Right::a 是因为它是最近被覆盖的属性。因此,Bottom.Top::aBottom.Right::Top::aBottom.Left::Top::aBottom.aBottom::Right::a 不同,但是我不知道这是否仅仅是因为名称查找还是与内存布局有关。有空我会看看你的例子 @DomFarolino 只有虚函数可以被覆盖。 @DomFarolino 隐藏数据成员?

以上是关于C++ 虚拟继承内存布局的主要内容,如果未能解决你的问题,请参考以下文章

多重虚拟继承中的虚拟表和内存布局

C++ 对象的内存布局

C++ 对象的内存布局

C++ 对象的内存布局

C++ 对象的内存布局

C++继承之C++中不同的继承体系