多态(Polymorphism):是面向对象编程(OOP)的三大核心特性之一,它允许同一操作作用于不同的对象时,可以产生不同的行为。
- 简单说,就是 “一个行为,多种形态”,通过统一的接口处理不同类型的对象,从而提高代码的灵活性和可扩展性。
多态的本质:“一种接口,多种实现”以下从 C++ 的角度,分编译时多态和运行时多态详细解析:
多态的核心价值:是解耦“接口使用”和“具体实现”:
- 调用者只需关注 “做什么”(接口),无需关心 “怎么做”(具体实现)
- 不同对象通过同一接口调用时,会自动执行自身的实现逻辑
C++ 中的多态分类:编译时多态(静态多态)和运行时多态(动态多态)核心区别在于:确定调用哪个实现的时机(编译阶段 vs 运行阶段)
------------多态的实现------------
① 静态多态的实现
静态多态:通过函数重载或模板,编译期根据调用参数确定具体执行的函数。
- 特点:行为确定于编译阶段,效率高,属于 “静态绑定”。
示例 1:
函数重载(同一作用域的同名函数,参数不同)
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; // 重载:根据参数类型/数量,编译期确定调用哪个函数 int Add(int a, int b) { return a + b; } double Add(double a, double b) { return a + b; } int main() { //1.编译期确定调用 Add(int, int) cout << Add(1, 2) << endl; //2.编译期确定调用 Add(double, double) cout << Add(1.5, 2.5) << endl; return 0; }在这里插入图片描述
示例 2:
模板(泛型编程,编译期生成具体代码)
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; // 模板:编译期根据 T 的类型,生成对应函数 template <typename T> T Add(T a, T b) { return a + b; } int main() { //1.编译期生成 Add<int>(int, int) cout << Add(1, 2) << endl; //2.编译期生成 Add<double>(double, double) cout << Add(1.5, 2.5) << endl; return 0; }在这里插入图片描述
② 动态多态的实现
动态多态:通过虚函数和继承,运行期根据对象的实际类型确定调用的函数。
- 特点:行为确定于运行阶段,支持动态扩展,属于 “动态绑定”。
核心条件(三要素):
- 继承:派生类继承基类。
- 虚函数:基类声明 virtual 函数,派生类重写(override)该虚函数。
- 基类指针/引用:通过基类指针或引用调用虚函数,指向派生类对象。
虚函数
虚函数(Virtual Function):是在基类中声明的、使用virtual关键字修饰的成员函数。
- 虚函数是 C++ 实现动态多态的核心机制。
- 虚函数的目的是为派生类提供一个可重写的接口。
- 虚函数会让程序在运行时根据对象的实际类型调用对应的函数实现。
任务1:基类声明虚函数
代码语言:javascript
AI代码解释
/*---------------------定义:“基类:Shape类”---------------------*/ class Shape { public: virtual void Draw() //基类声明 virtual,为派生类提供重写接口 { cout << "画一个形状" << endl; } };重写
重写(Override):是派生类重新实现基类中已声明的虚函数,让同一接口在不同派生类中有不同行为。
- 重写是 C++ 面向对象编程中实现动态多态的关键机制。
重写的严格规则(三同原则 + 协变返回)派生类重写基类虚函数时,需满足以下条件,否则会变成“隐藏”而非“重写”1. 三同原则(基本规则)
- 函数名相同:派生类函数名必须与基类虚函数完全一致
- 参数列表相同:参数的类型、数量、顺序必须完全一致
- 返回值类型相同:C++ 要求返回值类型严格一致,除非是协变返回类型
任务2:派生类重写虚函数
代码语言:javascript
AI代码解释
/*---------------------定义:“派生类:Circle类”---------------------*/ class Circle : public Shape { public: //1.派生类重写基类虚函数Draw() void Draw() { cout << "画一个圆" << endl; } }; /*---------------------定义:“派生类:Rectangle类”---------------------*/ class Rectangle : public Shape { public: //1.派生类重写基类虚函数Draw() void Draw() { cout << "画一个矩形" << endl; } };协变
2. 协变返回(特殊情况)
协变返回类型:是指若基类虚函数返回基类指针/引用,派生类重写的函数可返回派生类指针/引用,仍视为重写。
代码语言:javascript
AI代码解释
class Base { public: virtual Base* Clone() // 基类虚函数返回 Base* { return new Base(); } }; class Derived : public Base { public: Derived* Clone() // 派生类重写,返回 Derived*(协变返回) { return new Derived(); } };代码示例1:运行时多态的实现
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; /*---------------------定义:“基类:Shape类”---------------------*/ class Shape { public: //1.基类定义虚函数Draw() virtual void Draw() //基类声明 virtual,为派生类提供重写接口 { cout << "画一个形状" << endl; } }; /*---------------------定义:“派生类:Circle类”---------------------*/ class Circle : public Shape { public: //1.派生类重写基类虚函数Draw() void Draw() { cout << "画一个圆" << endl; } }; /*---------------------定义:“派生类:Rectangle类”---------------------*/ class Rectangle : public Shape { public: //1.派生类重写基类虚函数Draw() void Draw() { cout << "画一个矩形" << endl; } }; int main() { //1.基类指针指向派生类对象 Shape* shape1 = new Circle(); Shape* shape2 = new Rectangle(); //2.运行时根据对象实际类型,调用对应 Draw 函数 shape1->Draw(); // 输出:画一个圆(Circle 的 Draw) shape2->Draw(); // 输出:画一个矩形(Rectangle 的 Draw) delete shape1; delete shape2; return 0; }在这里插入图片描述
代码示例2:运行时多态的实现
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; /*---------------------定义:“基类:Person类”---------------------*/ class Person { public: virtual void BuyTicket() //基类声明 virtual,为派生类提供重写接口 { cout << "买票-全价" << endl; } }; /*---------------------定义:“派生类:Student类”---------------------*/ class Student : public Person { public: virtual void BuyTicket() //重写基类的虚函数 BuyTicket { cout << "买票-打折" << endl; } }; void Func(Person* ptr) //注意:通过基类指针调用虚函数,实际执行的是指针指向对象的重写版本 { ptr->BuyTicket(); /* 多态的核心逻辑: * 1.虽然调用的是 Person 指针的 BuyTicket, * 2.但实际执行的函数由 ptr 指向的对象的真实类型决定(Person 或 Student) * 3.这就是运行时多态(动态绑定)的体现 */ } int main() { //1.创建:“基类 + 派生类”的对象 Person ps; Student st; //2.调用 Func 函数,分别传入“Person + Student”对象的地址 Func(&ps); //ptr 是 Person* 类型,指向 Person 对象,调用 Person::BuyTicket Func(&st); //ptr 是 Person* 类型,但指向 Student 对象,调用 Student::BuyTicket(重写版本) return 0; }在这里插入图片描述
在这里插入图片描述
------------多态的拓展------------
1. override的意义与使用?
在 C++ 中,派生类重写基类虚函数时,即使派生类的函数不加
virtual关键字,也能构成重写。 这是因为:基类的虚函数被继承到派生类后,会自动保持 “虚函数” 的属性,无需重复声明 virtual
但这种写法不规范,原因有二:
- 可读性差:其他开发者阅读代码时,无法直观识别这是虚函数重写。
- 维护风险:若后续修改基类(如:删除虚函数关键字 ),派生类的重写逻辑会被破坏,且难以排查。
因此:C++11 及以上建议用 override 关键字显式标记重写,既规范又能让编译器帮你检查重写是否正确(如:函数名、参数不匹配时会报错 )
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; /*---------------------定义:“基类:Animal类”---------------------*/ class Animal { public: virtual void makeSound() { cout << "动物发出声音" << endl; } }; /*---------------------定义:“派生类:Dog类”---------------------*/ class Dog : public Animal { public: //写法1:隐式重写(不推荐) void makeSound() { cout << "汪汪汪!" << endl; } }; /*---------------------定义:“派生类:Cat类”---------------------*/ class Cat : public Animal { public: //写法2:显式用override标记(推荐) void makeSound() override { cout << "喵喵喵~" << endl; } }; /*---------------------测试函数:通过基类指针调用虚函数---------------------*/ void playSound(Animal* animal) { animal->makeSound(); //多态调用 } int main() { //1.创建:“基类 + 派生类”的对象 Animal generic; Dog dog; Cat cat; //2.调用playSound函数进行多态调用 playSound(&generic); // 输出:动物发出声音 playSound(&dog); // 输出:汪汪汪! playSound(&cat); // 输出:喵喵喵~ return 0; }在这里插入图片描述
2. final关键字怎么使用?
final 关键字:用于限制类的继承或虚函数的重写,增强代码的安全性和可维护性。
final关键字有两种用法:
- 修饰类:禁止该类被继承(即该类不能作为基类)
- 修饰虚函数:禁止派生类重写该虚函数
1. 修饰类(禁止继承)
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; class Base { // 基类成员 }; class Derived final : public Base //注意:使用 final 修饰,禁止被继承 { // 最终派生类成员 }; class IllegalDerived : public Derived //编译错误:无法从 final 类 Derived 派生 { // ... }; int main() { //1.创建“最终派生类”的对象 Derived d; //正确:可以正常创建 final 类的对象 cout << "成功创建 Derived 对象(final 类)" << endl; //2.创建“继承最终派生类”的对象 IllegalDerived i; //错误:无法实例化从 final 类派生的类 return 0; }在这里插入图片描述
2. 修饰虚函数(禁止重写)
代码语言:javascript
AI代码解释
#include <iostream> using namespace std; class Base { public: //1.声明虚函数,允许派生类重写 virtual void func() { cout << "Base::func()" << endl; } //2.声明虚函数并用 final 修饰,禁止派生类重写 virtual void finalFunc() final { cout << "Base::finalFunc()" << endl; } }; class Derived : public Base { public: //1.正常重写非 final 的虚函数 void func() override { cout << "Derived::func()" << endl; } //2.编译错误:void Derived::finalFunc() 重写 final 函数 void finalFunc() override { cout << "Derived::finalFunc()" << endl; } }; int main() { //1.测试虚函数重写 Base* ptr = new Derived(); ptr->func(); // 调用 Derived::func()(多态) //2.测试 final 函数 ptr->finalFunc(); // 错误:Derived::finalFunc() 无法重写 Base::finalFunc() delete ptr; return 0; }在这里插入图片描述
3. 析构函数是怎么进行重写的?
在 C++ 中,当基类的析构函数被声明为虚函数时,只要派生类定义了析构函数,无论是否添加virtual关键字,该派生类析构函数都会与基类析构函数构成重写。
- 尽管基类析构函数(如:
~Base())和派生类析构函数(如:~Derived())名字不同,看似不符合重写 “函数名相同” 的规则。 - 但实际上编译器会对析构函数名称做特殊处理,将编译后的名称统一处理为
destructor,从而实现重写机制。
通过下面的代码示例可以看到:
- 如果基类析构函数
~A()没有加virtual修饰,那么在执行delete p2时,只会调用基类A的析构函数,而不会调用派生类B的析构函数。 - 若派生类
B的析构函数~B()中包含资源释放的逻辑,这种情况就会导致资源无法正常释放,进而引发内存泄漏问题 。
www.dongchedi.com/article/7597217223819772478
www.dongchedi.com/article/7597215233471889944
www.dongchedi.com/article/7597216873696526910
www.dongchedi.com/article/7597217143041737241
www.dongchedi.com/article/7597214870441935385
www.dongchedi.com/article/7597214599947043353
www.dongchedi.com/article/7597214580846477886
www.dongchedi.com/article/7597216071082738201
www.dongchedi.com/article/7597214433031078424
www.dongchedi.com/article/7597214537498362392
www.dongchedi.com/article/7597215399566361150
www.dongchedi.com/article/7597215658752868888
www.dongchedi.com/article/7597215102077141528
www.dongchedi.com/article/7597214696924004889
www.dongchedi.com/article/7597213042329895448
www.dongchedi.com/article/7597215125493400126
www.dongchedi.com/article/7597212587801018905
www.dongchedi.com/article/7597214580846215742
www.dongchedi.com/article/7597214267869692440
www.dongchedi.com/article/7597213056480969278
www.dongchedi.com/article/7597212812516639257
www.dongchedi.com/article/7597212812516868633
www.dongchedi.com/article/7597213320844182041
www.dongchedi.com/article/7597211160895046206
www.dongchedi.com/article/7597211076186374681
www.dongchedi.com/article/7597212587801477657
www.dongchedi.com/article/7597210839670080062
www.dongchedi.com/article/7597210276412899864
www.dongchedi.com/article/7597211030086926872
www.dongchedi.com/article/7597211160895078974
www.dongchedi.com/article/7597209997126238744
www.dongchedi.com/article/7597209064238039577
www.dongchedi.com/article/7597211030087287320
www.dongchedi.com/article/7597209862904480318
www.dongchedi.com/article/7597209319725253145
www.dongchedi.com/article/7597208525277987353
www.dongchedi.com/article/7597208525278151193
www.dongchedi.com/article/7597210268858958398
www.dongchedi.com/article/7597209475426435609
www.dongchedi.com/article/7597209772429476377
www.dongchedi.com/article/7597201951176213017
www.dongchedi.com/article/7597201687174562366
www.dongchedi.com/article/7597199724889997849
www.dongchedi.com/article/7597199550092657177
www.dongchedi.com/article/7597200248943329816
www.dongchedi.com/article/7597199001863701017
www.dongchedi.com/article/7597198298541834777
www.dongchedi.com/article/7597200591446000190
www.dongchedi.com/article/7597199968348357145
www.dongchedi.com/article/7597199312984162841
www.dongchedi.com/article/7597199429019861566
www.dongchedi.com/article/7597196791863902782
www.dongchedi.com/article/7597197725960110616
www.dongchedi.com/article/7597197533550920217
www.dongchedi.com/article/7597196766895079960
www.dongchedi.com/article/7597197878439756313
www.dongchedi.com/article/7597196909912031768
www.dongchedi.com/article/7597195764053492248
www.dongchedi.com/article/7597196370181030424
www.dongchedi.com/article/7597195961618121241
www.dongchedi.com/article/7597195004385182232
www.dongchedi.com/article/7597196281857442366
www.dongchedi.com/article/7597195368090075673
www.dongchedi.com/article/7597195809683505689
www.dongchedi.com/article/7597194439940932158
www.dongchedi.com/article/7597194164794933822
www.dongchedi.com/article/7597194060553749016
www.dongchedi.com/article/7597195414877995544
www.dongchedi.com/article/7597194911112479256
www.dongchedi.com/article/7597194219174068761
www.dongchedi.com/article/7597192718418756120
www.dongchedi.com/article/7597191743318065689
www.dongchedi.com/article/7597194069471003161
www.dongchedi.com/article/7597193401016500760
www.dongchedi.com/article/7597192147254772286
www.dongchedi.com/article/7597192394672505406
www.dongchedi.com/article/7597190301329080894
www.dongchedi.com/article/7597188998091833881
www.dongchedi.com/article/7597190006675243582
www.dongchedi.com/article/7597189866363372056