虚函数表
虚函数表的结构
很容易找到文档,参考
查表过程
疑问
虚函数表中存着一个个最终动态确定下来的虚函数的地址,在调用虚函数的时候,是如何在虚函数表中找到当前调用这个函数的地址的?
编译器遇到一个虚函数定义的时候就记住一个名字,遇到一个虚函数调用的时候也记住一个名字,编译完成后,一个一个统计每个类的虚表里应该有多少个虚函数,然后把统计到的虚函数一个一个分配到格子里,得到一个名字到偏移的对应关系,再把所有记住名字的地方的名字一个一个替换成偏移。
例子
| 12
 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进行调试:
 以下汇编中加入了注释,可以看出d->func2是直接对vptr加上一个常量偏移(这里是8,即偏移一个表项),因此可以验证:在编译完成后,每个虚函数在虚函数表中的偏移已经确定了
以下汇编中加入了注释,可以看出d->func2是直接对vptr加上一个常量偏移(这里是8,即偏移一个表项),因此可以验证:在编译完成后,每个虚函数在虚函数表中的偏移已经确定了
| 12
 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
 
 |