C++ 中 vtable 的函数解析

Function resolution from vtable in C++

在阅读了更多有关名称修改的信息后,我对 vtable 感到困惑。 例如:

class Base
{
public:
    virtual void print()
    {
    }
};

class A : public Base
{
public:
    void hello()
    {
        ....
    }

    void print()
    {
    }
};

A obj;
obj.hello();

Base* test = new A();
test->print();

根据我的理解,名称管理 obj.hello() 调用现在将转换为类似 _ZASDhellov(&obj) 的内容

  1. 如何从 vtable 调用此虚函数?
  2. 我的疯狂猜测 test->__vtable[_ZASDprintv](&test(dynamic cast to derived???)) 是正确的吗?
  3. 如何从 vtable 解析函数名称?

首先,vtables 在任何方面都不是 C++ 语言的一部分,而是特定编译器使用的实现细节。下面我描述一种常用的方式。

其次,您的函数 hello 不是虚函数。要使其成为虚拟的,您只需在声明前添加 virtual

假设它现在是虚拟的:您的猜测非常接近。事实上,vtable(一个指针存储在虚拟 class 的每个实例中)是一个函数指针数组。在其中查找特定函数的方式是按其序号。 A 中第一个声明的虚函数是其 vtable 中的第一个条目,第二个是第二个条目,依此类推。如果 A 有一个基础 class,A 在 table 中的第一个(非覆盖)虚函数的索引将是 n+1,其中 n是其基class的最后一个虚函数的索引。如果 A 有多个基数 class,则它们的条目按照它们声明为 A 的基数 class 的顺序排在 A 的条目之前。

如果A使用虚继承,图就复杂一点,除非你特别感兴趣我就不细说了。

更新: 我将根据要求为虚拟继承案例添加一个非常简短的描述。如果 ABase 作为虚拟基础 class,Avtable 将在最开始(在函数地址之前)存储字节偏移量Base 的数据从 A 对象开始。这是必要的,因为与正常继承不同,基 class 的数据不在派生 class 的数据之前 - 而是在派生 class 的数据之后。因此实际上,任何对 Base 中定义的虚函数的函数调用都必须使其 this 指针偏移该量。此外,Base 必须有自己的 vtable 指针,就在它希望找到它的数据的开头。因此完整的 A 对象将包含两个 vtable 指针而不是一个。第二个指针指向的实际 vtable 将与第一个 vtable 指针相同,除了提前跳过上述偏移条目(以便任何 Base 代码使用 vtable 会在预期的开头找到第一个虚函数)。除了这些差异之外,vtable 本身与以前相同。