scd*_*dmb 3 c++ virtual-inheritance vtable memory-layout vptr
有这个代码:
#include <iostream>
class Base
{
int x;
};
class Derived : virtual public Base
{
int y;
};
int main()
{
std::cout << sizeof(Derived) << std::endl; // prints 12
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我已经读过,当某个类被虚拟继承时,会为类派生创建空虚表,因此内存布局如下:
Derived::ptr to empty vtable
Derived::y
Base::x
Run Code Online (Sandbox Code Playgroud)
它是 12 个字节。问题是 -如果没有任何虚拟方法,这个空的vtable 的目的是什么,它是如何使用的?
Derived需要某种方式来知道Base子对象在哪里。对于虚拟继承,基类的相对位置相对于派生类的位置不是固定的:它可以位于完整对象中的任何位置。
考虑一个更典型的例子,涉及钻石继承。
struct A
{
int a;
};
struct B1 : virtual A
{
int b1;
};
struct B2 : virtual A
{
int b2;
};
struct C : B1, B2
{
int c;
};
Run Code Online (Sandbox Code Playgroud)
在这里,B1和B2都虚拟地派生自A,因此在 中C,恰好只有一个A子对象。双方B1并B2需要知道如何找到A子对象(以便他们可以访问a成员变量,或其他成员A如果我们定义它们)。
这就是虚函数表被用于在这种情况下:既B1和B2将包含的偏移的虚函数表A的子对象。
为了演示编译器如何实现上述菱形继承示例,请考虑以下由 Visual C++ 11 开发人员预览版生成的类布局和虚拟表。
class A size(4):
+---
0 | a
+---
class B1 size(12):
+---
0 | {vbptr}
4 | b1
+---
+--- (virtual base A)
8 | a
+---
class B2 size(12):
+---
0 | {vbptr}
4 | b2
+---
+--- (virtual base A)
8 | a
+---
class C size(24):
+---
| +--- (base class B1)
0 | | {vbptr}
4 | | b1
| +---
| +--- (base class B2)
8 | | {vbptr}
12 | | b2
| +---
16 | c
+---
+--- (virtual base A)
20 | a
+---
Run Code Online (Sandbox Code Playgroud)
以及以下虚拟表:
B1::$vbtable@:
0 | 0
1 | 8 (B1d(B1+0)A)
B2::$vbtable@:
0 | 0
1 | 8 (B2d(B2+0)A)
C::$vbtable@B1@:
0 | 0
1 | 20 (Cd(B1+0)A)
C::$vbtable@B2@:
0 | 0
1 | 12 (Cd(B2+0)A)
Run Code Online (Sandbox Code Playgroud)
注意偏移量是相对于vtable的地址而言的,注意对于为B1和B2子对象生成的两个vtable C,偏移量是不同的。
(另请注意,这完全是一个实现细节——其他编译器可能会以不同的方式实现虚函数和基函数。此示例演示了它们的一种实现方式,并且它们非常普遍地以这种方式实现。)
| 归档时间: |
|
| 查看次数: |
3210 次 |
| 最近记录: |