前言
多态是面向对象编程的三大核心特性(封装、继承、多态)之一,它使得同一接口可以呈现出不同的行为,极大地提升了代码的灵活性和可扩展性。在 C++ 中,多态的实现与虚函数、虚表等机制紧密相关,其底层逻辑涉及编译期与运行期的不同处理方式。
本文将系统梳理 C++ 多态的概念、实现条件、核心机制(虚函数与虚表),并深入解析多态在继承场景下的表现,同时结合典型问题与示例代码,帮助读者全面理解多态的本质与应用。
多态的概念
通俗来说,多态即多种形态,具体指完成某个行为时,不同的对象会产生出不同的状态,从而实现更灵活和方便的调用。
多态的定义和实现
虚函数
被 virtual 修饰的类成员函数称为虚函数。
虚函数的重写 (覆盖)
派生类中有一个跟基类完全相同的虚函数(即返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写或者覆盖了基类的虚函数。
例外情况:
- 协变:基类与派生类虚函数返回值类型可以不同,但必须是父子关系的指针或引用。
- 关键字省略:派生类重写虚函数可以不加
virtual(建议加上)。
析构函数是否为虚函数? 析构函数可以是虚函数。如果基类析构函数声明为虚函数,则构成虚函数重写。这是为了防止通过基类指针删除派生类对象时,只调用基类析构函数而导致资源泄漏。
多态的构成条件
- 必须通过基类的指针或者引用调用虚函数。
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
注意:多态调用看的是指向的对象,普通的调用看的是当前的类型。
为什么必须使用父类的指针或引用?
- 不能是父类对象:不会拷贝子类的虚表和其他特有成员,父类对象无法感知子类的存在。
- 不能是子类指针或引用:编译器限制访问父类中没有的成员,避免类型安全问题。
子类虚表的构建: 子类继承父类时,会先复制一份父类的虚表。如果子类没有重写父类的虚函数,虚表中对应函数指针指向父类实现;若子类重写了某个虚函数,则用子类自己的虚函数地址覆盖虚表中对应的函数指针。
override 和 final(C++11 提出)
final
- 修饰虚函数:表示该虚函数不能再被重写。
- 修饰类:直接禁止任何类继承它。
virtual void text() final {} // 前有无 virtual 不重要
override
- 作用:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写则编译报错。
class Person {
public:
virtual void text() {}
};
: Person {
:
{}
};


