题目
原文:
How do virtual functions work in C++?
译文:
C++中的虚函数是如何工作的?
分析
虚函数是在调用时动态绑定的,这就依赖于虚函数表进行工作。
如果在一个基类中,有函数被关键词virtual进行修饰, 那么一个虚函数表就会被自动构建起来去保存这个类中虚函数的地址。同时编译器会为这个类的所有对象添加一个隐藏指针vptr指向虚函数表。
如果在派生类中没有重写虚函数, 那么派生类中虚表存储的是父类虚函数的地址;相反地,如果在派生类中重写父类的虚函数,派生类的虚表中将覆盖父类在该虚函数的地址。
每当虚函数被调用时, 虚表会决定具体去调用哪个函数。因此,C++中的动态绑定是通过虚函数表机制进行的。当我们用基类指针指向派生类时,虚表指针vptr指向派生类的虚函数表。 这个机制可以保证派生类中的虚函数被调用到。
例子
1,例子一–单继承
class Base {
public:
virtual void f() {cout<<"base::f"<<endl;}
virtual void g() {cout<<"base::g"<<endl;}
virtual void h() {cout<<"base::h"<<endl;}
};
class Derive : public Base{
public:
void g() {cout<<"derive::g"<<endl;}
};
此时基类Base的虚表是:
而派生类Derive的虚表是:
2,例子二–多继承
可以看出,多继承中派生类的虚函数重写,会导致所有继承的基函数对应虚表中的地址被覆盖,而派生类新加的函数g1()则只出现在vtable中一次。
补充
腾讯的一道关于虚表的面试题–
问题:
“有一个类指针,指向类实例化的对象,在程序的运行过程中,这个类指针指向的对象崩溃了,这个类指针的虚函数表被破坏了,如何定位这个问题?”
解答:
类的虚函数表被破坏是不可能的,因为这个 vtable 是放在只读数据区rodata。修改 vtable 会导致 segment fault段错误(数组越界、内存泄漏、空指针等错误会引起这个报错),这样检查 coredump(包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息等的一个文件) 文件就知道哪里造成的vtable修改。
类的虚表指针是可能被修改的,比如说访问虚表的指针出现错误越界,或者误成为空指针、悬挂指针等等。
最后我们来看一下虚函数使用中的所有内存分配情况: