derived中vtable的理解类
Understanding of vtable in derived classes
我试图通过虚拟 table 和继承来理解一些低级的东西。
当您通过继承两个 classes 并添加新的虚函数来创建新的 class 时,vptr 将存储在哪里?
在我看来,编译器在那种情况下会执行一些 'vptr-optimization'。我正在努力弄清楚。
假设,我们有以下结构:
struct A
{
int a;
virtual void fa();
};
struct B
{
double b;
virtual void fb();
};
struct C : A, B
{
char c;
virtual void fa();
virtual void fb();
virtual void fc();
};
在 x86 和 align=4 的情况下,内存中的 A
和 B
将如下所示:
+------+------+
A: | vptr | a |
+------+------+
sizeof(A) = 4 + 4 = 8
+------+------+------+------+
B: | vptr | b |
+------+------+------+------+
sizeof(B) = 8 + 8 = 16
但是当我尝试重新组装 C
时,我得到了这个:
+------+------+------+------+------+------+------+
C: | vptr | a | vptr | b | c |
+------+------+------+------+------+------+------+
but sizeof(C) = 32
С с;
(C*)&c; // 0x100
(B*)&c; // 0x108
(A*)&c; // 0x100
&c.a; // 0x104
&c.b; // 0x110
&c.c; // 0x118
那么C
的vptr在哪里呢?我可以假设编译器合并了不同的虚拟 tables(例如 A
和 C
的 vptr),但在那种情况下为什么 sizeof(C)
returns sizeof(A)
+ sizeof(B)
+ sizeof(alligned_char)
+ sizeof(vptr)
struct D : public C {}
有同样的故事 - D
没有 vptr。
我使用的编译器是msvc 2012 x86.
编译器必须兼顾简单性,因为基础 classes 需要存在于对象中。
+------+---------+----+
| A | B | C |
+------+---------+----+
所以
- A 需要存在,就好像它不是派生的一样。
- B 需要存在,就好像它不是派生的一样。
- C 有了新的自由。
来自 A 和 B 的虚函数将针对派生的 class C 的实现进行修补。
C 将向(可能)现有的第一个元素 A
s vtable 添加虚函数。
A 的基础 vtable
+------+
| A:fa |
+------+
vtable for A
in the derived class C
+------+
| C:fa | // implemented by derived class.
+------+
| C:fb | // any calls to fb need to be sent to `C`'s implementation
+------+
| C:fc | // any calls to fc can be overridden by vtable.
+------+
vtable for B
in the derived class C
+------+
| C:fb | // overridden, but no need to add fc, fa to this table.
+------+
我认为对齐规则导致 C
的大小被填充,因此对齐敏感双精度正确对齐(确保 C
的数组正确对齐)。
B
的大小是 vptr
的大小 (4) 和填充以确保 double
对齐 (4) 和 [=23= 的大小] (8)
我试图通过虚拟 table 和继承来理解一些低级的东西。
当您通过继承两个 classes 并添加新的虚函数来创建新的 class 时,vptr 将存储在哪里?
在我看来,编译器在那种情况下会执行一些 'vptr-optimization'。我正在努力弄清楚。
假设,我们有以下结构:
struct A
{
int a;
virtual void fa();
};
struct B
{
double b;
virtual void fb();
};
struct C : A, B
{
char c;
virtual void fa();
virtual void fb();
virtual void fc();
};
在 x86 和 align=4 的情况下,内存中的 A
和 B
将如下所示:
+------+------+
A: | vptr | a |
+------+------+
sizeof(A) = 4 + 4 = 8
+------+------+------+------+
B: | vptr | b |
+------+------+------+------+
sizeof(B) = 8 + 8 = 16
但是当我尝试重新组装 C
时,我得到了这个:
+------+------+------+------+------+------+------+
C: | vptr | a | vptr | b | c |
+------+------+------+------+------+------+------+
but sizeof(C) = 32
С с;
(C*)&c; // 0x100
(B*)&c; // 0x108
(A*)&c; // 0x100
&c.a; // 0x104
&c.b; // 0x110
&c.c; // 0x118
那么C
的vptr在哪里呢?我可以假设编译器合并了不同的虚拟 tables(例如 A
和 C
的 vptr),但在那种情况下为什么 sizeof(C)
returns sizeof(A)
+ sizeof(B)
+ sizeof(alligned_char)
+ sizeof(vptr)
struct D : public C {}
有同样的故事 - D
没有 vptr。
我使用的编译器是msvc 2012 x86.
编译器必须兼顾简单性,因为基础 classes 需要存在于对象中。
+------+---------+----+
| A | B | C |
+------+---------+----+
所以
- A 需要存在,就好像它不是派生的一样。
- B 需要存在,就好像它不是派生的一样。
- C 有了新的自由。
来自 A 和 B 的虚函数将针对派生的 class C 的实现进行修补。
C 将向(可能)现有的第一个元素 A
s vtable 添加虚函数。
A 的基础 vtable
+------+
| A:fa |
+------+
vtable for A
in the derived class C
+------+
| C:fa | // implemented by derived class.
+------+
| C:fb | // any calls to fb need to be sent to `C`'s implementation
+------+
| C:fc | // any calls to fc can be overridden by vtable.
+------+
vtable for B
in the derived class C
+------+
| C:fb | // overridden, but no need to add fc, fa to this table.
+------+
我认为对齐规则导致 C
的大小被填充,因此对齐敏感双精度正确对齐(确保 C
的数组正确对齐)。
B
的大小是 vptr
的大小 (4) 和填充以确保 double
对齐 (4) 和 [=23= 的大小] (8)