C++中vtable的函数解析

Function resolution from vtable in C++

本文关键字:函数 vtable C++      更新时间:2023-10-16

在阅读了更多关于名称篡改的内容后,我对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(指向它的指针与虚拟类的每个实例一起存储(是一个函数指针数组。在其中查找特定函数的方式是通过其序数。A中第一个声明的虚拟函数是其vtable中的第一个条目,第二个是第二个条目,依此类推。如果A有基类,则A在表中的第一(非重写(虚拟函数的索引将是n+1,其中n是其基类的最后一个虚拟函数的索引。如果A有多个基类,则它们的条目按照声明为A基类的顺序位于A的条目之前。

如果A使用虚拟继承,情况会比这复杂一些,除非你特别感兴趣,否则我不会详细说明。

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