为什么 sizeof(Base) 与 sizeof(Derived) 不同
因为编译器引入了对齐。
这是架构相关的,但为了简单起见,我假设我们指的是 64 位架构。
Scenario 64 bit / Clang 8.0.
Base 类型的对齐方式是8 字节:
alignOfBase(): # @alignOfBase()
mov eax, 8
ret
Base的布局由变量成员(int)和虚拟表(vtptr)组成。
如果我们假设一个“通用”架构,其中:
-
int 是 4 字节大小。
-
vtptr 是一个指针。在 64 位架构上是 8 字节大小。
如您所料,我们应该有一个 4 + 8 = 12 的总和。
但是,我们需要记住Base 的对齐方式是8 bytes。因此,连续的Base 类型应该存储在 8 的位置倍数中。
为了保证这一点,编译器为Base 引入了填充。这就是Base 大小为 16 字节的原因。
例如,如果我们考虑 2 个连续的 Base(base0 和 base1)没有填充:
0: vtptr (base 0) + 8
8: int (base 0) + 4
12: vtptr (base 1) + 8 <--- Wrong! The address 12 is not multiple of 8.
20: int (base 1) + 4
带内边距:
0: vtptr (base 0) + 8
8: int (base 0) + 4+4 (4 padding)
16: vtptr (base 1) +8 <--- Fine! The adress 16 is multiple of 8.
24: int (base 1) +4+4 (4 padding)
Derived 类型也是如此。
Derived的布局应该是:vtptr + int + int,即8 + 4 + 4 = 16。
Derived 的对齐方式也是8:
alignOfDerived(): # @alignOfDerived()
mov eax, 8
ret
确实,在这种情况下,不需要引入填充来保持Derived 与内存对齐。布局大小将与实际大小相同。
0: vtptr (Derived 0)
8: int (Derived 0)
12: int (Derived 0)
16: vtptr (Derived 1) <---- Fine. 16 is multiple of 8.
24: int (Derived 1)
28: int (Derived 1)