虚函数在哪里使用 vpointers to vtables 来解析方法调用,非虚方法存储在哪里以及它们是如何解析的?

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

A class 定义

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

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

但是,class

的一个实例
class B
{
    void BFunc1(){}
    void BFunc2(){}
    void BFunc3(){}
};

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

对于每一个真正在某处使用的非虚函数,函数的代码被发送到目标文件,如果代码来自包含的头文件并在多个翻译单元中使用,则可能在多个文件中。此外,符号也被插入到目标文件中。如果您是这样的 linux 用户,您可以使用 nm 查看已定义 methods/fuctions/... 的 table:

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++ 中使用 "One Definition Rule" 的原因)。顺便说一句:如果你有多个使用相同标签的定义,linker 也只放置一个并且没有错误消息。您的程序的行为由 link 命令定义。非常糟糕的事情!回到弱符号,如果我们多次将相同的方法存储在目标文件中,linker 不应该发出错误。 linker 现在可以简单地选择其中之一,而不会出现任何错误消息。例如,main 函数在 table 中被标记为 'T'。如果您多次使用 T 标记相同的符号,linker 将为多个定义的函数发出错误消息。

你看,符号在目标文件中的地址是0000000,也就是说,它目前没有地址。地址将在静态执行 link 时间内或程序启动时的动态 link 时间内重新定位。

如您所见,虚函数的存储方式也是一样的,没有任何区别。也可以使用 A::AVirtualFunc 调用方法本身而无需跳过 vtable.

你还可以看到,vtable 本身是目标文件的一部分,并标记为 V。您也可以在多个目标文件中使用相同的 vtable。 linker 在最后 link.

中也只拿了其中一个