虚函数表
在C++中,虚函数表(vtable) 是实现运行时多态性(即动态多态性)的关键机制。它使得基类指针或引用可以调用派生类的重写方法,而无需在编译时确定实际调用哪个函数。这种机制在对象模型中发挥着重要作用。
# 一、虚函数表(vtable)
虚函数表是由编译器自动生成的一个数据结构,存在于包含虚函数的类中。它本质上是一个函数指针数组,存储了该类的所有虚函数的地址。
- vtable的内容:对于一个包含虚函数的类,编译器会为该类生成一个虚函数表。表中存储了指向该类虚函数的指针。如果派生类重写了基类的虚函数,派生类的虚函数表中相应位置会被更新为指向派生类重写函数的指针。
# 二、虚函数表指针(vptr)
- vptr 是每个对象中隐藏的指针,它指向该对象所属类的虚函数表(vtable)。通常,这个指针是对象的一部分,存储在对象内存布局的最前面。
- 当一个对象被创建时,vptr被初始化为指向该对象所属类的vtable。
- 在调用虚函数时,通过vptr查找到对象所属类的vtable,然后通过vtable找到对应的虚函数指针并调用。
# 三、虚函数表的工作原理
当一个类包含虚函数时,编译器为该类生成虚函数表,类的每个对象都有一个vptr指向它所属的虚函数表。通过这个机制,可以实现动态绑定。
# 示例:
#include <iostream>
class Base {
public:
virtual void func1() {
std::cout << "Base func1" << std::endl;
}
virtual void func2() {
std::cout << "Base func2" << std::endl;
}
virtual ~Base() = default; // 虚析构函数
};
class Derived : public Base {
public:
void func1() override {
std::cout << "Derived func1" << std::endl;
}
void func2() override {
std::cout << "Derived func2" << std::endl;
}
};
int main() {
Base* b = new Derived();
b->func1(); // 调用 Derived::func1
b->func2(); // 调用 Derived::func2
delete b; // 调用 Derived 析构函数,然后是 Base 析构函数
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
解释:
- 当
Derived
类对象被创建时,Derived
对象的vptr
会被设置为指向Derived
类的虚函数表。 - 虚函数表
vtable
包含指向Derived::func1
和Derived::func2
的指针,而不是Base::func1
和Base::func2
。 - 当通过基类指针
b
调用虚函数func1()
和func2()
时,实际调用的是Derived
类的实现。
# 四、虚函数表的特性和注意事项
指针开销:每个包含虚函数的类对象会多一个
vptr
指针,这会增加对象的内存开销。性能开销:通过虚函数表实现动态绑定会稍微降低性能,因为需要通过
vptr
查找函数指针,而不是直接调用函数。虚析构函数:如果一个类包含虚函数,通常应将析构函数定义为虚函数,以确保通过基类指针删除派生类对象时,派生类的析构函数能够被正确调用。
多重继承:在多重继承中,每个基类的虚函数表都会生成独立的vtable,派生类对象中可能会有多个vptr来指向不同基类的vtable。
# 五、虚函数表的布局(图解)
以 Base
和 Derived
为例,虚函数表的布局可能如下所示:
Base 类的 vtable:
+------------------+ | Base::func1() | +------------------+ | Base::func2() | +------------------+ | Base::~Base() | +------------------+
1
2
3
4
5
6
7Derived 类的 vtable:
+------------------+ | Derived::func1() | +------------------+ | Derived::func2() | +------------------+ | Derived::~Base() | // 如果没有重写,仍指向Base的析构函数 +------------------+
1
2
3
4
5
6
7对象中的 vptr:
Base
对象的vptr
指向Base
类的 vtable。Derived
对象的vptr
指向Derived
类的 vtable。
# 总结
虚函数表是C++中实现运行时多态性的核心机制,通过vtable和vptr,C++能够在运行时根据对象的实际类型动态调用合适的函数。理解虚函数表及其工作原理对于掌握C++面向对象编程和调试复杂的多态性代码非常重要,也经常出现在C++的面试中。
编辑 (opens new window)
上次更新: 2024/09/13, 11:59:12