虚拟函数在哪里使用 vpointer to vtable 来解析方法调用,非虚拟方法存储在哪里以及如何解析它们?

Where virtual functions use vpointers to vtables to resolve the method call, where are non-virtual methods stored and how are they resolved?

本文关键字:虚拟 在哪里 方法 存储 何解析 调用 to vpointer vtable 函数      更新时间:2023-10-16

具有定义的类

class A
{
void AFunc1(){}
void AFunc2(){}
void AFunc3(){}
virtual void AVirtualFunc1(){}
};

将具有 4 个字节的sizeof()值,因为隐藏的vpointer成员指向具有指向方法的指针的共享vtable

但是,类的实例

class B
{
void BFunc1(){}
void BFunc2(){}
void BFunc3(){}
};

将只有 1 个字节的sizeof()值,因为不需要vpointer,而且不存在vtable。如果是这种情况,BFunc1()BFunc2()BFunc3()函数存储在哪里,对象实例如何引用它们?

对于在某处实际使用的每个非虚函数,函数的代码都会发送到目标文件,如果代码来自包含的标头并在多个翻译单元中使用,则可能在多个文件中。此外,符号也会插入到对象文件中。您可以看到已定义方法/功能/...如果您是这样的 Linux 用户,请使用nm

nm main.o |c++filt

U __cxa_atexit
U __dso_handle
0000000000000062 t _GLOBAL__sub_I_main
0000000000000000 T main
0000000000000024 t __static_initialization_and_destruction_0(int, int)
0000000000000000 W A::AVirtualFunc1()
0000000000000000 W A::AFunc1()
0000000000000000 r __gnu_cxx::__default_lock_policy
U std::ios_base::Init::Init()
U std::ios_base::Init::~Init()
0000000000000000 b std::__ioinit
0000000000000000 V typeinfo for A
0000000000000000 V typeinfo name for A
0000000000000000 V vtable for A
U vtable for __cxxabiv1::__class_type_info

如您所见A::AFunc1()被定义为weak符号。它被定义为弱,因为我们可以在不同的翻译单元中有多个实例,但我们知道,它们都是相同的。(这就是我们在C++中有"一个定义规则")。顺便说一句:如果您有多个使用相同的标签的定义,则链接器也只放置一个并且没有错误消息。程序的行为由链接顺序定义。这是一件非常糟糕的事情!回到弱符号,如果我们在目标文件中存储了多次相同的方法,链接器不应该发出错误。链接器现在可以简单地选取其中一个,而不会显示任何错误消息。例如,main函数在表中标记为"T"。如果多次使用T标记相同的符号,链接器将为多个定义的函数发出错误消息。

您会看到,对象文件中符号的地址是 0000000。这意味着,它目前没有地址。地址将在静态可执行文件的链接期间或程序启动时的动态链接时间内重新定位。

如您所见,虚拟函数也以相同的方式存储,完全没有区别。该方法本身也可以使用A::AVirtualFunc调用,而无需跳过 vtable。

您还可以看到,vtable 本身是目标文件的一部分,并标记为V。您还可以在多个对象文件中具有相同的 vtable。链接器在最终链接中也只采用其中一个。