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