虚拟基础对象位于属于该对象的内存块中的某个位置(size = sizeof(object) 的内存)。由于多个不同类型的子对象可以通过多种方式组合,但必须共享同一个基础对象,因此每个子对象都需要一个偏移指针来找出虚拟基础对象。在没有虚拟继承的情况下,用于查找相应基础对象的偏移量在每个类类型的编译时都是固定的。
sizeof 值取决于您的编译器和机器,但以下假设很常见:
假设:指针大小为 4 个字节
假设:类大小四舍五入为 4 个字节的倍数
sizeof(A): 8 -> 1 pointer to vtable (virtual method)
+ 3 chars -> 4+3=7
-> round up to 8
sizeof(B): 20 -> 8 + 1 pointer to vtable (virtual method)
+ 1 offset pointer to virtual base
+ 3 chars -> 8 + 4 + 4 + 3 = 19
-> round up to 20
sizeof(C): 32 -> 20 + 1 pointer to vtable (virtual method)
+ 1 offset pointer to virtual base
+ 3 chars
-> 20 + 4 + 4 + 3 = 31 // this calculation refers to an older
-> round up to 32 // version of the question's example
// where C had B as base class
计算是猜测的,因为真正的计算必须确切地知道编译器是如何工作的。
问候,奥利弗
为什么需要额外的偏移量指针的更多细节:
例子:
class B : virtual public A {...};
class C : virtual public A {...};
class D1 : public B {...};
class D2 : public B, C {...};
D1 可能的内存布局:
A
B
D1
D2 可能的内存布局:
A
C
B
D2
在第二种情况下,子对象 B 需要另一个偏移量来找到它的基础 A
D2 类型的对象由一个内存块组成,其中包含所有父对象部分,即 D2 类型的对象的内存块有一个用于 A 成员变量、C 成员变量、B 成员变量和D2 成员变量。这些部分的顺序取决于编译器,但该示例显示,对于多重虚拟继承,需要一个偏移指针,该指针在 对象的总内存块内指向虚拟基础对象。这是必需的,因为 B 类的方法只知道一个指向 B 的this指针,并且必须以某种方式计算 A 内存部分相对于this指针的位置。
计算sizeof(D):
sizeof(D): 36 -> A:3 chars + A:vtable
+ B:3 chars + B:vtable + B:virtual base pointer
+ C:3 chars + C:vtable + C:virtual base pointer
+ D:3 chars + D:vtable
= 3 + 4
+ 3 + 4 + 4
+ 3 + 4 + 4
+ 3 + 4
= 36
上面的计算可能是错误的;-) ...
我不确定 D 部分是否有自己的 vtable 指针(这完全取决于编译器)。
我现在认为可能是 D 部分使用其父类的 vtable 指针条目,并且 4 个额外字节用于对齐每个部分(8 个字节的倍数):
所以这个计算可能更正确:
sizeof(D): 36 -> A:3 chars + A:vtable + A:alignment
+ B:3 chars + B:vtable + B:virtual base pointer + B:alignment
+ C:3 chars + C:vtable + C:virtual base pointer + C:alignment
+ D:3 chars + D:alignment
= 3 + 4 + 1
+ 3 + 4 + 4 + 1
+ 3 + 4 + 4 + 1
+ 3 + 1
= 36