虚函数是C++实现运行时多态的核心机制,通过virtual声明、vtable动态绑定实现基类指针调用派生类重写函数;析构函数应为虚函数,构造函数不能为虚函数,纯虚函数定义抽象类。

虚函数是 C++ 中实现运行时多态的核心机制,它允许基类指针或引用调用派生类中重写的函数,具体执行哪个版本由对象的实际类型决定,而不是指针或引用的静态类型。
虚函数怎么声明和使用
在基类中用 virtual 关键字修饰成员函数,就把它变成虚函数。派生类中同名、同参数、同返回类型的函数会自动成为虚函数(即使不写 virtual),也可以显式加上 override(推荐)来增强可读性和编译检查。
- 虚函数必须是类的成员函数,不能是全局函数或静态成员函数
- 构造函数不能是虚函数(对象还没完全生成)
- 析构函数建议声明为虚函数,尤其是当类可能作为基类被继承时,防止派生类资源泄漏
多态是怎么靠虚函数工作的
C++ 编译器为含有虚函数的类生成一个虚函数表(vtable),每个对象开头隐含一个指向该表的指针(vptr)。vtable 中按声明顺序存放虚函数地址。当通过基类指针调用虚函数时,程序查 vptr → 找 vtable → 取对应函数地址 → 跳转执行,这个过程在运行时完成,所以叫“动态绑定”或“晚绑定”。
- 普通非虚函数调用在编译期就确定地址,叫“早绑定”
- 只有通过基类指针或引用调用虚函数,才会触发多态;直接用对象调用(如 obj.func())仍是静态绑定
- 纯虚函数(virtual void func() = 0;)让类变成抽象类,不能实例化,强制派生类实现
常见误区和注意事项
虚函数不是万能的,用错反而引发问题。比如,把不该多态的函数设为虚函数会增加对象体积(每个对象多一个 vptr)和调用开销(间接跳转);反过来,该虚却不虚,就无法体现多态行为。
立即学习“C++免费学习笔记(深入)”;
- 不要在构造/析构函数中调用虚函数:此时 vptr 指向当前正在构造或析构的类的 vtable,不会调到派生类的重写版本
- 返回类型协变允许派生类虚函数返回更具体的类型(如基类返回 Base*,派生类可返回 Derived*),但参数列表必须严格一致
- 虚函数可以有默认参数,但默认值由静态类型决定,容易混淆,建议避免在虚函数中使用默认参数
一个简单可运行的例子
下面代码展示了虚函数如何支撑多态:
#includeusing namespace std; class Animal { public: virtual void speak() { cout << "Animal makes a sound\n"; } virtual ~Animal() = default; // 虚析构很重要 };
class Dog : public Animal { public: void speak() override { cout << "Dog barks\n"; } };
class Cat : public Animal { public: void speak() override { cout << "Cat meows\n"; } };
int main() { Animal a1 = new Dog(); Animal a2 = new Cat(); a1->speak(); // 输出 Dog barks a2->speak(); // 输出 Cat meows delete a1; delete a2; }
输出结果取决于 new 出来的实际对象类型,而不是 Animal* 的类型 —— 这就是多态的本质。











