C++ 虚函数表
2025-04-01

虚函数表(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 {}; // virtual 关键字表示虚继承
class C : virtual public A {}; // virtual 关键字表示虚继承
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 的数据成员(唯一) |

注意

  1. 如果使用多态特性,一定要给基类的虚构函数加上 virtual 关键字,否则一个基类指针指向派生类对象时,析构触发时只会调用基类的析构,派生类的析构函数没有调用,造成了内存泄露。