虚函数表(vtable)是什么?
虚函数表(vtable)用于实现 C++
的运行时多态,每个含有虚函数的类都会自动生成一个虚函数表,表中存放所有虚函数的地址。对象中存储指向虚函数表的指针(vptr),运行时通过该指针调用对应虚函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Base { public: virtual void foo() { std::cout << "Base::foo()" << std::endl; } virtual void bar() { std::cout << "Base::bar()" << std::endl; } };
class Derived : public Base { public: void foo() override { std::cout << "Derived::foo()" << std::endl; } };
|
内存布局示意图:
1 2 3 4 5 6
| Derived 对象 | vptr | --> Derived 的 vtable
Derived 的 vtable | foo | --> Derived::foo() | bar | --> Base::bar()
|
多重继承对 vtable 的影响
多重继承时,对象内会存在多个 vptr,每个 vptr 指向不同基类的
vtable。
1 2 3
| class A { virtual void f() {}}; class B { virtual void g() {}}; class C : public A, public B {};
|
内存布局示意图:
1 2 3
| C 对象 | vptr_A | --> A 的 vtable | vptr_B | --> B 的 vtable
|
虚继承对 vtable 的影响
虚继承会解决菱形继承中重复基类实例的问题,通过引入虚基表(vbptr),记录虚基类相对对象的偏移。
1 2 3 4
| class A { virtual void f() {}}; class B : virtual public A {}; class C : virtual public A {}; class D : public B, public C {};
|
虚继承对象布局:
1 2 3 4 5 6 7 8 9 10 11 12
| D 对象: | vptr_B | --> B 的 vtable | vbptr_B | --> B 的 vbtable(指向共享的 A) | B 的数据成员 |
| vptr_C | --> C 的 vtable | vbptr_C | --> C 的 vbtable(指向共享的 A) | C 的数据成员 |
| D 的数据成员 |
| A 的数据成员(唯一) |
|
注意
- 如果使用多态特性,一定要给基类的虚构函数加上
virtual
关键字,否则一个基类指针指向派生类对象时,析构触发时只会调用基类的析构,派生类的析构函数没有调用,造成了内存泄露。