在开始时应该只有一个大小为void *的零(除非没有使用RTTI编译)。实际上不必这样,但通常是这样的,稍后我会解释。
(至少gcc源自的)ABI的虚表看起来像:
class_offset
type_info
first_virtual_function
second_virtual_function
etc.
如果编译时没有启用RTTI,则type_info
可能为NULL
(0
)。
上面的class_offset
解释了为什么在那里看到零。这是所属类中的类偏移量。也就是说,有:
class A { virtual meth() {} };
class B { virtual meth() {} };
class C: public A, public B { virtual meth() {} };
这将导致在主类 C
中,A
从位置 0
开始,B
从位置 4
(或 8
)开始。
指针的作用是让您能够从任何类指针中找到拥有对象的指针。因此,对于任何“主”类,它将始终为 0
,但对于在 C
上下文中有效的 B
类虚表,它将为 -4
或 -8
。您实际上需要检查 C 的 VTable(后半部分),因为编译器通常不会单独生成 VTable:
_ZTV1C:
.quad 0
.quad _ZTI1C
.quad _ZN1CD1Ev
.quad _ZN1CD0Ev
.quad _ZN1C4methEv
.quad -8
.quad _ZTI1C
.quad _ZThn8_N1CD1Ev
.quad _ZThn8_N1CD0Ev
.quad _ZThn8_N1C4methEv
在早期的编译器中,使用偏移量来计算调用方法之前所属类的实际指针。但由于直接在所属类上调用方法时会减慢速度,现代编译器更倾向于生成存根,直接减去偏移量并跳转到方法的主要实现(从方法名称可以猜出-注意 8 ):
<code>_ZThn8_N1C4methEv:
subq $8, %rdi
jmp _ZN1C4methEv
</code>