虚函数表
虚函数表(vtable,Virtual Table)是 C++ 实现运行时多态的核心机制之一。虚函数表是编译器在处理带有虚函数的类时创建的一个数据结构,主要用于支持动态绑定,即根据对象的实际类型在运行时决定调用哪个函数。这是 C++ 中实现多态的关键。
虚函数表的工作原理
当一个类包含虚函数时,编译器会为该类创建一张虚函数表。虚函数表本质上是一个指针数组,每个元素都是指向该类虚函数的地址。具体来说:
虚函数表包含指向该类虚函数的函数指针,当你调用某个虚函数时,程序会通过虚函数表来查找实际要调用的函数实现。
每个类有一张虚函数表,类中的每个虚函数在虚函数表中都有一个对应的入口。
对于每个包含虚函数的类的对象,编译器会在对象的内存布局中添加一个虚函数表指针(vptr),该指针指向对象所属类的虚函数表。
虚函数表的基本结构
假设有以下类层次结构:
#include <iostream>
class Base {
public:
virtual void func1() {
std::cout << "Base::func1" << std::endl;
}
virtual void func2() {
std::cout << "Base::func2" << std::endl;
}
};
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;
return 0;
}在这个例子中,Base 类有两个虚函数 func1 和 func2,派生类 Derived 重写了它们。
虚函数表的内部运作:
类的虚函数表(vtable):
Base类的虚函数表存储了Base::func1和Base::func2的地址。Derived类的虚函数表则存储了Derived::func1和Derived::func2的地址,分别覆盖了Base类的对应函数。
虚函数表的结构如下:
Base 类的 vtable
函数地址
func1Base::func1func2Base::func2Derived 类的 vtable
函数地址
func1Derived::func1func2Derived::func2对象的虚函数表指针(vptr):
当创建一个
Derived类的对象时,编译器会在该对象中添加一个指向Derived类虚函数表的指针vptr。通过
Base *b = new Derived();,指针b指向Derived对象,而这个对象的vptr指向的是Derived类的虚函数表,因此调用func1()和func2()时会根据vptr指向的表找到Derived::func1和Derived::func2,并调用它们。
运行时多态的实现:
在调用 b->func1() 时:
程序通过
b的vptr找到指向的Derived类的虚函数表。在虚函数表中查找
func1的位置,得到Derived::func1的地址并进行调用。类似地,
func2也会调用Derived::func2。
虚函数表的特点
每个类有一张虚函数表:
每个包含虚函数的类会有自己的虚函数表。
如果一个类没有虚函数,则没有虚函数表,也不会有虚函数表指针。
虚函数表指针(vptr):
每个对象有一个虚函数表指针
vptr,该指针在对象创建时由构造函数初始化,指向对象所属类的虚函数表。对象在运行时通过
vptr指针来查找虚函数的实现。
继承与虚函数表:
派生类会继承基类的虚函数表,并可以覆盖虚函数。如果派生类没有重写基类的虚函数,那么它会直接使用基类的虚函数地址。
如果派生类重写了虚函数,则它会在自己的虚函数表中覆盖相应的条目。
性能开销:
虚函数调用比普通函数调用稍微慢一些,因为每次调用虚函数时,程序需要先通过
vptr查找虚函数表,然后找到正确的函数指针进行调用。这是一种间接调用。然而,这种性能损失通常是微不足道的,只有在高性能场景下(如游戏开发、嵌入式系统等)可能会显得重要。
虚函数表的内存布局
一个类的虚函数表是静态存储的,所有对象共享这张表。一个包含虚函数的对象通常内存布局如下:
对象内存布局:
| vptr | object_data |其中:
vptr是一个指向类虚函数表的指针。object_data是对象本身的数据成员。
多继承和虚函数表
C++ 支持多继承,如果一个类继承了多个有虚函数的父类,那么该类可能会有多个虚函数表。例如,假设有两个基类 Base1 和 Base2,它们各自都有虚函数,如果 Derived 类同时继承自 Base1 和 Base2,那么 Derived 类的
Last updated