vtbl

虚函数表

虚函数表的结构

很容易找到文档,参考

查表过程

疑问

虚函数表中存着一个个最终动态确定下来的虚函数的地址,在调用虚函数的时候,是如何在虚函数表中找到当前调用这个函数的地址的?

网上的回答

编译器遇到一个虚函数定义的时候就记住一个名字,遇到一个虚函数调用的时候也记住一个名字,编译完成后,一个一个统计每个类的虚表里应该有多少个虚函数,然后把统计到的虚函数一个一个分配到格子里,得到一个名字到偏移的对应关系,再把所有记住名字的地方的名字一个一个替换成偏移。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

class base {
public:
virtual void func1() {std::cout << "I'm in class base func1" << std::endl;}
};

class derived :public base {
public:
virtual void func2() {std::cout << "I'm in class derived func2" << std::endl;}
};


int main() {
derived *d = new derived();
d->func2();
return 0;
}

通过gdb进行调试:

vtbl_lookup以下汇编中加入了注释,可以看出d->func2是直接对vptr加上一个常量偏移(这里是8,即偏移一个表项),因此可以验证:在编译完成后,每个虚函数在虚函数表中的偏移已经确定了

1
2
3
4
5
6
7
8
│    0x5555555551f2 <main()+41>                  mov    %rbx,-0x18(%rbp)  # rbx是d的值,将变量d存放于栈顶:-0x18(%rbp) 
│ 0x5555555551f6 <main()+45> mov -0x18(%rbp),%rax
│ 0x5555555551fa <main()+49> mov (%rax),%rax # rax是vptr,指向vtbl
│ 0x5555555551fd <main()+52> add $0x8,%rax # 加上常量rax指向虚函数表的第二项
│ 0x555555555201 <main()+56> mov (%rax),%rdx # rdx是第二个表项的值,其指向func2的地址
│ 0x555555555204 <main()+59> mov -0x18(%rbp),%rax
│ 0x555555555208 <main()+63> mov %rax,%rdi
│ 0x55555555520b <main()+66> call *%rdx # 调用func2

vtbl
http://example.com/vtbl/
作者
Yw
发布于
2024年5月8日
许可协议