虚函数通过vtable和vptr实现运行时多态,基类指针调用派生类函数;virtual声明虚函数,override确保正确重写,防止签名错误。

在C++中,虚函数和多态是面向对象编程的核心机制之一。它们让基类指针或引用能够调用派生类的函数,实现运行时动态绑定。理解其底层原理以及正确使用 virtual 和 override 关键字,对编写可维护、安全的继承体系至关重要。
虚函数与多态的基本概念
当一个类中的函数被声明为 virtual,该函数就成为虚函数。派生类可以重写(覆盖)这个函数。通过基类指针或引用调用该函数时,程序会根据实际对象的类型决定调用哪个版本——这就是动态多态。
示例:
#includeclass Animal { public: virtual void speak() { std::cout << "Animal speaks\n"; } }; class Dog : public Animal { public: void speak() override { std::cout << "Dog barks\n"; } };
int main() { Animal* ptr = new Dog(); ptr->speak(); // 输出: Dog barks delete ptr; return 0; }
如果没有 virtual,调用的是基类的 speak();有了虚函数,调用的是 Dog 的实现。
立即学习“C++免费学习笔记(深入)”;
虚函数表(vtable)与虚函数指针(vptr)机制
C++ 实现多态的核心是虚函数表(vtable)和每个对象内部的虚函数指针(vptr)。
- 每个定义了虚函数的类,编译器会生成一张虚函数表,其中存放该类所有虚函数的地址。
- 每个该类的对象都会包含一个隐藏的指针(vptr),指向其所属类的虚函数表。
- 当通过基类指针调用虚函数时,程序通过 vptr 找到实际类型的 vtable,再查表调用对应函数。
这种机制发生在运行时,因此称为动态分发。虽然带来了一定的性能开销(一次间接寻址),但提供了极大的灵活性。
注意:虚函数表是类级别的,每个类一份;vptr 是对象级别的,每个对象都有一个。
virtual 关键字的使用规范
virtual 用于声明基类中的虚函数。一旦函数被声明为虚,它在派生类中也自动成为虚函数,即使不显式写出 virtual。
- 通常只在基类中首次声明时加
virtual。 - 析构函数应始终声明为虚函数,如果类可能被继承。否则删除派生类对象时,可能不会调用派生类的析构函数,导致资源泄漏。
推荐写法:
class Base {
public:
virtual ~Base() = default; // 虚析构函数
virtual void foo();
};
override 关键字的作用与优势
override 是 C++11 引入的关键字,用于显式标记派生类中意图重写的函数。它的主要作用是防止误写。
- 如果函数签名与基类虚函数不匹配(如参数不同、const 属性不同),编译器会报错。
- 提高代码可读性,明确表达“这是重写”的意图。
错误示例(未使用 override):
class Base {
public:
virtual void process(int x);
};
class Derived : public Base {
public:
void process(double x) { } // 无意中重载,而非重写
};
此时 Derived::process(double) 并没有重写基类函数,而是一个新的重载函数。若本意是重写,这就成了隐蔽的bug。
正确做法:
class Derived : public Base {
public:
void process(int x) override { } // 编译器确保正确重写
};
如果签名错误,加上 override 后编译器会直接报错,避免运行时行为异常。
基本上就这些。掌握虚函数机制和关键字用法,能有效提升C++继承体系的安全性和可维护性。










