C++核心编程 4 类和对象 - 多态(7)
4.7 多态
4.7.1 多态的基本概念
多态是C++面向对象三大特性之一,多态分为两类:静态多态、动态多态。
静态多态:函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行时多态
区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
#include<iostream> using namespace std; //多态 //动物类 class Animal { public: virtual void speak() { cout << "动物在说话" << endl; } }; //猫类 class Cat :public Animal { public: //重写 函数返回值类型 函数名 参数列表 完全相同 void speak() { cout << "小猫在说话" << endl; } }; //狗类 class Dog :public Animal { public: void speak() { cout << "小狗在说话" << endl; } }; //执行说话的函数 //地址早绑定 在编译阶段确定了函数地址 //如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,既地址晚绑定 改为虚函数即可 //动态多态满足条件:1、有继承关系 2、子类重写父类的虚函数 //动态多态使用 父类的指针或引用 执行子类对象 void doSpeak(Animal &animal) //Animal &animal = cat -> animal的引用指向cat 父类的引用指向子类 { animal.speak(); } void test1() { Cat cat; doSpeak(cat); Dog dog; doSpeak(dog); } int main() { test1(); system("pause"); return 0; }
总结:多态满足条件
1、有继承关系
2、子类重写父类中的虚函数
多态使用条件
1、父类指针或引用
#include<iostream> #include<string> using namespace std; //分别用普通写法和多态技术 实现计算器 //普通写法 class Calculator { public: int getResult(string oper) { if (oper == "+") { return m_Num1 + m_Num2; } else if(oper == "-") { return m_Num1 - m_Num2; } else if (oper == "*") { return m_Num1 * m_Num2; } else if (oper == "/") { return m_Num1 / m_Num2; } //如果想扩展新功能,需要修改源码 //在真实开发中 提倡 开闭原则 //开闭原则:对扩展进行开放,对修改进行关闭 } int m_Num1; //操作数1 int m_Num2; //操作数2 }; //多态技术 //多态好处:1、组织结构清晰 2、可读性强 3、对于前后期的维护和扩展高 //实现计算器抽象类 class AbstractCalculator { public: virtual int getResult() { return 0; } int m_Num1; int m_Num2; }; //加法计算器类 class AddCalculator :public AbstractCalculator { public: virtual int getResult() // virtual可加可不加 { return m_Num1 + m_Num2; } }; //减法计算器类 class SubCalculator :public AbstractCalculator { public: int getResult() // virtual可加可不加 { return m_Num1 - m_Num2; } }; //乘法计算器类 class MulCalculator :public AbstractCalculator { public: int getResult() // virtual可加可不加 { return m_Num1 * m_Num2; } }; //除法计算器类 class ChuCalculator :public AbstractCalculator { public: int getResult() // virtual可加可不加 { return m_Num1 / m_Num2; } }; void test1() { Calculator c; //创建一个计算器对象 c.m_Num1 = 10; c.m_Num2 = 10; cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl; cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl; cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl; cout << c.m_Num1 << "/" << c.m_Num2 << "=" << c.getResult("/") << endl; } void test2() { //多态使用条件 //父类指针或者引用指向子类对象 //加法运算 AbstractCalculator* a = new AddCalculator; a->m_Num1 = 100; a->m_Num2 = 100; cout << a->m_Num1 << "+" << a->m_Num2 << "=" << a->getResult() << endl; //用完后记得销毁 delete a; //减法运算 a = new SubCalculator; a->m_Num1 = 100; a->m_Num2 = 100; cout << a->m_Num1 << "-" << a->m_Num2 << "=" << a->getResult() << endl; //用完后记得销毁 delete a; //乘法运算 a = new MulCalculator; a->m_Num1 = 100; a->m_Num2 = 100; cout << a->m_Num1 << "*" << a->m_Num2 << "=" << a->getResult() << endl; //用完后记得销毁 delete a; //减法运算 a = new ChuCalculator; a->m_Num1 = 100; a->m_Num2 = 100; cout << a->m_Num1 << "/" << a->m_Num2 << "=" << a->getResult() << endl; //用完后记得销毁 delete a; } int main() { //test1(); test2(); system("pause"); return 0; }
4.7.3 纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数1改为纯虚函数。
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0;
当类中有了纯虚函数,这个类也称为抽象类。
抽象类特点:
1、无法实例化对象
2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include<iostream> using namespace std; class Base { public: //纯虚函数 //只要有一个纯虚函数,这个类称为抽象类 //抽象类特点: //1、无法实例化对象 //2、抽象的子类必须重写父类中的纯虚函数,否则也属于抽象类 virtual void func() = 0; }; class Son :public Base { public: virtual void func() { cout << "func函数调用" << endl; }; }; void test1() { //Base b; //抽象类是无法实例化对象 //new Base; //抽象类是无法实例化对象 Son s; //子类必须重写父类中的纯虚函数,否则无法实例化对象 Base* base = new Son; base->func(); } int main() { test1(); system("pause"); return 0; }
案例 做饮料
#include<iostream> using namespace std; //多态案例2 制作饮品 class AbstractDrinking { public: virtual void Boil() = 0; //煮水 virtual void Brew() = 0; //冲泡 virtual void PourInCup() = 0; //倒入杯中 virtual void PutSomething() = 0; //加入佐料 void MakeDrink() //制作饮品 { Boil(); Brew(); PourInCup(); PutSomething(); } }; //制作咖啡 class Coffee :public AbstractDrinking { public: //煮水 virtual void Boil() { cout << "煮农夫山泉" << endl; } //冲泡 virtual void Brew() { cout << "冲泡咖啡" << endl; } //倒入杯中 virtual void PourInCup() { cout << "将咖啡倒入杯中" << endl; } //加入佐料 virtual void PutSomething() { cout << "加入糖和牛奶" << endl; } }; //制作茶叶水 class Tea :public AbstractDrinking { public: //煮水 virtual void Boil() { cout << "煮崂山泉水" << endl; } //冲泡 virtual void Brew() { cout << "冲泡铁观音" << endl; } //倒入杯中 virtual void PourInCup() { cout << "将茶水倒入杯中" << endl; } //加入佐料 virtual void PutSomething() { cout << "加入枸杞" << endl; } }; //制作 void doWork(AbstractDrinking * abs) //AbstractDrinking * abs = new Coffee { abs->MakeDrink(); delete abs; //堆区内存手动开辟,手动释放 } void test1() { //制作咖啡 doWork(new Coffee); cout << "-----------------" << endl; doWork(new Tea); } int main() { test1(); system("pause"); return 0; }
4.7.5 虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决办法:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
1、可以解决父类指针释放子类对象
2、都需要具体的函数实现
区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法: virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0
#include<iostream> #include<string> using namespace std; //虚析构和纯虚析构 class Animal { public: Animal() { cout << "Animal 构造函数的调用" << endl; } ////利用虚析构可以解决 父类指针释放子类对象时不干净的问题 //virtual ~Animal() //{ // cout << "Animal 析构函数的调用" << endl; //} //纯虚析构 需要声明也需要实现 //有了纯虚析构之后 这个类也属于抽象类 无法实例化对象 virtual ~Animal() = 0; //如果没有第27-30行的类外声明,代码将无法运行 因为没实现就不能执行父类的析构 //纯虚函数 virtual void speak() = 0; }; Animal::~Animal() { cout << "Animal 纯虚析构函数的调用" << endl; } class Cat :public Animal { public: Cat(string name) { cout << "Cat构造函数的调用" << endl; m_Name = new string(name); } virtual void speak() { cout << *m_Name << "小猫在说话" << endl; } ~Cat() { if (m_Name != NULL) { cout << "Cat析构函数调用" << endl; delete m_Name; m_Name = NULL; } } string *m_Name; }; void test1() { Animal * animal = new Cat("Tom"); animal->speak(); //父类指针在析构时 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏,解决办法为把父类中的析构函数改为虚析构 delete animal; } int main() { test1(); system("pause"); return 0; }
案例2 电脑组装
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!