虚函数表(通常简称为 vtable)是 C++ 用于实现多态行为的一种机制。当一个类定义了虚函数或者继承了虚函数,编译器会为该类生成一个虚函数表。下面详细介绍虚函数表及其工作原理:
1. 什么是虚函数表?
虚函数表是一个存放指向虚函数的指针数组。每一个有虚函数的类(或者从有虚函数的类继承而来的类)都有一个相关联的虚函数表。
2. 对象与虚函数表:
每个有虚函数的对象都包含一个指向其类的虚函数表的指针。这个指针通常被称为 vptr。
3. 如何工作?
当调用一个对象的虚函数时,编译器使用对象的 vptr 来定位类的虚函数表。接着,从虚函数表中找到相应的虚函数指针并调用该函数。这个过程是在运行时进行的,因此可以实现多态行为。
4. 继承和虚函数表:
-
当一个类继承自另一个有虚函数的类,并且没有重写任何虚函数,该类的对象将使用父类的虚函数表。
-
当派生类重写了基类的虚函数,派生类的虚函数表中该函数的入口会被更新为指向派生类版本的函数。
-
如果派生类添加了新的虚函数,它们会被添加到虚函数表的末尾。
5. 为什么需要虚函数表?
C++ 使用虚函数表来支持动态多态,允许在基类指针或引用上调用适当的派生类函数,而不仅仅是基类定义的函数。
6. 性能考虑:
虚函数调用通常比非虚函数调用稍慢,因为需要额外的间接跳转。然而,这种开销在绝大多数应用中都是可以接受的。
虚函数还增加了对象的大小(因为每个对象需要一个 vptr)以及类的大小(因为需要存储虚函数表)。
7. 示例:
#include <iostream>class Base {
public:virtual void foo() { std::cout << "Base::foo()" << std::endl; }
};class Derived : public Base {
public:void foo() override { std::cout << "Derived::foo()" << std::endl; }
};int main() {Base* obj = new Derived();obj->foo(); // 输出 "Derived::foo()"delete obj;return 0;
}
在这个示例中,当我们通过基类指针 obj
调用 foo
函数时,实际调用的是 Derived
类中的版本。这是因为虚函数表机制找到了 Derived
类版本的 foo
函数,并执行了它。
总的来说,虚函数表是 C++ 实现动态多态的基石,它允许我们在基类指针或引用上调用适当的派生类方法。