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::a
或 Left::a
?
在您的测试 2 中,Right
和 Left
共享同一个共同父级 Top
。所以Bottom
中只有一个子对象Top
,因此,只有一个相同的Top::a
。
有趣的是,您在测试 2 中在 Right
中引入了成员 a
。这是一个a
,它不同于继承自Top
的a
。它是一个独特的成员,只是“巧合”地与另一个成员同名。因此,如果您通过Right
访问a
,Right::a
将隐藏Top::a
(顺便说一下,Bottom::Top::a
、Right::Top::a
、Left::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::a
、Bottom.Right::Top::a
和Bottom.Left::Top::a
与Bottom.a
和Bottom::Right::a
不同,但是我不知道这是否仅仅是因为名称查找还是与内存布局有关。有空我会看看你的例子
@DomFarolino 只有虚函数可以被覆盖。
@DomFarolino 隐藏数据成员?以上是关于C++ 虚拟继承内存布局的主要内容,如果未能解决你的问题,请参考以下文章