多态:多种状态——一般说多态都指动态多态
多态的优点(体现于练手1):
● 代码组织结构清晰
● 可读性强
● 利于前期和后期的扩展以及维护
多态分为两类:
● 静态多态:函数重载和运算符重载属于静态多态,复用函数名
● 动态多态:派生类(子类)和虚函数实现运行时多态
静态多态和动态多态区别:
● 静态多态的函数地址早绑定——编译阶段确定函数地址
● 动态多态的函数定制晚绑定——运行阶段确定函数地址
例子:
①地址早绑定:
1 #include <iostream> 2 3 using namespace std; 4 5 class Animal{ 6 public: 7 void speak(){ 8 cout << "动物正在说话!" << endl; 9 } 10 }; 11 12 class Cat : public Animal{ 13 public: 14 void speak(){ 15 cout << "猫正在说话!" << endl; 16 } 17 }; 18 19 //地址早绑定 20 void doSpeak(Animal &animal){ //Animal & animal = cat; 父类引用指向子类 21 animal.speak(); //地址已被确定,无论输入什么,都调用函数animal.speak() 22 } 23 24 void test01(){ 25 Cat cat; 26 doSpeak(cat); 27 } 28 29 int main(){ 30 test01(); 31 system("pause"); 32 return 0; 33 }
输出:动物正在说话
原因:void doSpeak(Animal &animal){animal.speak();}中,已经在编译阶段确定了函数doSpeak的地址,因此无论传什么,都执行animal.speak这个函数。
②地址晚绑定:
1 #include <iostream> 2 3 using namespace std; 4 5 class Animal{ 6 public: 7 //虚函数——>实现地址晚绑定 8 virtual void speak(){ 9 cout << "动物正在说话!" << endl; 10 } 11 }; 12 13 class Cat : public Animal{ 14 public: 15 void speak(){ 16 cout << "猫正在说话!" << endl; 17 } 18 }; 19 20 void doSpeak(Animal &animal){ //Animal & animal = cat; 父类引用指向子类 21 animal.speak(); //此时函数地址不能被提前确定,要等传入数据后才能确定 22 } 23 24 void test01(){ 25 Cat cat; 26 doSpeak(cat); 27 } 28 29 int main(){ 30 test01(); 31 system("pause"); 32 return 0; 33 }
综上:
动态多态的满足条件:
1、有继承关系
2、子类重写父类的虚函数(重写:返回类型、函数名、形参列表这三个条件完全一致的函数)
关于第二点:子类的virtual关键字可写可不写,但是父类一定要写
动态多态的使用:
※父类的指针或者引用执行子类对象(上面①②的例子均采用引用方法)
多态的底层原理::
采用②的例子
当Animal类中的函数void speak()不加virtual关键字时,在内存中占一个字节
当Animal类中的函数void speak()加上virtual关键字时,在内存中占四个字节(指针占4个字节[32位下])
说明Animal类的结果发生了改变,函数virtual void speak()变成了一个指针(vfptr)。。。
练手1(采用普通写法和多态,设计实现俩个操作数进行运算的计算器类):
普通写法:
1 #include <iostream> 2 3 using namespace std; 4 5 //普通实现 6 class Calculator{ 7 public: 8 int getResult(string oper){ 9 if(oper == "+"){ 10 return N1 + N2; 11 } 12 else if(oper == "-"){ 13 return N1 - N2; 14 } 15 else if(oper == "*"){ 16 return N1 * N2; 17 } 18 else if(oper == "/"){ 19 return N1 / N2; 20 } 21 else{ 22 return 0; 23 } 24 //若需要扩展新功能,需修改源码 25 //开发中提倡开闭原则:对扩展进行开放,对修改进行关闭 26 } 27 28 int N1; 29 int N2; 30 }; 31 32 void test01(){ 33 Calculator c; 34 c.N1 = 10; 35 c.N2 = 10; 36 cout << c.N1 << " + " << c.N2 << " = " << c.getResult("+") << endl; 37 cout << c.N1 << " - " << c.N2 << " = " << c.getResult("-") << endl; 38 cout << c.N1 << " * " << c.N2 << " = " << c.getResult("*") << endl; 39 cout << c.N1 << " / " << c.N2 << " = " << c.getResult("/") << endl; 40 } 41 42 int main(){ 43 test01(); 44 system("pause"); 45 return 0; 46 }
多态写法:
1 #include <iostream> 2 3 using namespace std; 4 5 //多态实现计算器 6 //实现计算机抽象类(父类) 7 class AbstractCalculator{ 8 public: 9 virtual int getResult(){ 10 return 0; 11 } 12 13 int N1; 14 int N2; 15 }; 16 //加法类 17 class AddCalculator : public AbstractCalculator{ 18 public: 19 int getResult(){ 20 return N1 + N2; 21 } 22 }; 23 //减法类 24 class SubCalculator : public AbstractCalculator{ 25 public: 26 int getResult(){ 27 return N1 - N2; 28 } 29 }; 30 //乘法类 31 class MulCalculator : public AbstractCalculator{ 32 public: 33 int getResult(){ 34 return N1 * N2; 35 } 36 }; 37 //除法类 38 class DivCalculator : public AbstractCalculator{ 39 public: 40 int getResult(){ 41 return N1 / N2; 42 } 43 }; 44 45 void test(){ 46 //加法 47 AbstractCalculator * abc = new AddCalculator; //父类指针指向子类对象 48 abc->N1 = 10; 49 abc->N2 = 10; 50 cout << abc->N1 << " + " << abc->N2 << " = " << abc->getResult() << endl; 51 delete abc; //用完后记得销毁堆区数据 52 53 //减法 54 abc = new SubCalculator; //上面只是销毁堆区数据,指针的类型没有变 55 abc->N1 = 10; 56 abc->N2 = 10; 57 cout << abc->N1 << " - " << abc->N2 << " = " << abc->getResult() << endl; 58 delete abc; 59 60 //乘法 61 abc = new MulCalculator; 62 abc->N1 = 10; 63 abc->N2 = 10; 64 cout << abc->N1 << " * " << abc->N2 << " = " << abc->getResult() << endl; 65 delete abc; 66 67 //除法 68 abc = new DivCalculator; 69 abc->N1 = 10; 70 abc->N2 = 10; 71 cout << abc->N1 << " / " << abc->N2 << " = " << abc->getResult() << endl; 72 delete abc; 73 } 74 75 int main(){ 76 test(); 77 system("pause"); 78 return 0; 79 }
纯虚函数和抽象类:
概念:在多态中,通常父类中虚函数的实现是毫无意义的,主要是调用子类重写的内容
基于此,可以把虚函数改为纯虚函数,当类中有纯虚函数,那么这个类也称为抽象类
纯虚函数语法:
抽象类的特点:
● 抽象类无法实例化对象
● 子类必须重写抽象类中的纯虚函数,否则子类也属于抽象类
参考上面例子的虚函数,改为virtual void/int func() = 0; 即可实现纯虚函数,实现抽象类。
练手2(采用多态实现制作茶水或者咖啡):
流程:煮水,冲茶/咖啡,倒杯,加柠檬/糖和牛奶
1 #include <iostream> 2 3 using namespace std; 4 5 class Drinks{ 6 public: 7 //煮水 8 virtual void Boil() = 0; 9 //冲泡 10 virtual void Brew() = 0; 11 //倒杯 12 virtual void Cup() = 0; 13 //加料 14 virtual void others() = 0; 15 16 //制作 17 void make(){ 18 Boil(); 19 Brew(); 20 Cup(); 21 others(); 22 } 23 }; 24 25 //咖啡 26 class Coffee :public Drinks{ 27 public: 28 //煮水 29 virtual void Boil(){ //virtual选填 30 cout << "煮恒河水" << endl; 31 } 32 //冲泡 33 virtual void Brew(){ //virtual选填 34 cout << "泡咖啡" << endl; 35 } 36 //倒杯 37 virtual void Cup(){ //virtual选填 38 cout << "倒万人杯" << endl; 39 } 40 //加料 41 virtual void others(){ //virtual选填 42 cout << "加入玛萨拉" << endl; 43 } 44 }; 45 46 //茶叶 47 class Tea :public Drinks{ 48 public: 49 //煮水 50 virtual void Boil(){ //virtual选填 51 cout << "煮普通水" << endl; 52 } 53 //冲泡 54 virtual void Brew(){ //virtual选填 55 cout << "泡茶" << endl; 56 } 57 //倒杯 58 virtual void Cup(){ //virtual选填 59 cout << "倒万人杯" << endl; 60 } 61 //加料 62 virtual void others(){ //virtual选填 63 cout << "加入芦荟汁" << endl; 64 } 65 }; 66 67 //制作 68 void doWork(Drinks * abs){ 69 abs->make(); 70 delete abs; //记得释放堆区数据 71 } 72 73 void test01(){ 74 //制作咖啡 75 doWork(new Coffee); //相当于Drinks * abs = new Coffee; 76 cout << "-------------------------" << endl; 77 doWork(new Tea); //相当于Drinks * abs = new Tea; 78 } 79 80 int main(){ 81 test01(); 82 system("pause"); 83 return 0; 84 }
虚析构和纯虚析构:
引入:使用多态时候,若子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。。
解决:把父类中的析构函数改为虚析构或者纯虚析构。。
虚析构和纯虚析构的共性:
● 可以解决父类指针释放子类对象的问题。。。
● 都需要有具体的函数实现。。。
不同点:
● 类中有纯虚析构,则此类属于抽象类,无法实例化对象。。。
语法:
注意点:无论是虚析构还是纯虚析构,都需要写具体的实现,纯虚析构的具体实现要写在类外。。
综上所述:
①如果子类中没有堆区数据,父类可以不写虚析构或纯虚析构
②父类中的虚析构和纯虚析构,在子类中不用重写,可以直接调用
练手3(电脑组装)看懂即理解:
要求↓:
由CPU,显卡,内存条三类组成
把每个零件封装成抽象类,并且提供不同厂商生产的零件(Intel和AMD)
创建电脑类提供让电脑工作的函数,并调用每个零件工作的接口
目标:测试时组装三台不同的电脑进行工作。。
1 #include <iostream> 2 3 using namespace std; 4 5 //抽象零件类 6 //CPU抽象类 7 class CPU{ 8 public: 9 //抽象计算函数 10 virtual void calculate() = 0; 11 }; 12 //显卡抽象类 13 class VideoCard{ 14 public: 15 //抽象显示函数 16 virtual void display() = 0; 17 }; 18 //内存抽象类 19 class Memory{ 20 public: 21 //抽象计算函数 22 virtual void storage() = 0; 23 }; 24 25 //厂商 26 //Intel 27 class ICPU: public CPU{ 28 virtual void calculate(){ 29 cout << "I家CPU开始计算了" << endl; 30 } 31 }; 32 class IVideoCard: public VideoCard{ 33 virtual void display(){ 34 cout << "I家显卡开始显示了" << endl; 35 } 36 }; 37 class IMemory: public Memory{ 38 virtual void storage(){ 39 cout << "I家内存条开始运作了" << endl; 40 } 41 }; 42 43 //AMD 44 class ACPU: public CPU{ 45 virtual void calculate(){ 46 cout << "A家CPU开始计算了" << endl; 47 } 48 }; 49 class AVideoCard: public VideoCard{ 50 virtual void display(){ 51 cout << "A家显卡开始显示了" << endl; 52 } 53 }; 54 class AMemory: public Memory{ 55 virtual void storage(){ 56 cout << "A家内存条开始运作了" << endl; 57 } 58 }; 59 60 //========================================// 61 //电脑类 62 class Computer{ 63 public: 64 Computer(CPU * cpu, VideoCard * vc, Memory * me){ 65 mcpu = cpu; 66 mvc = vc; 67 mme = me; 68 } 69 //工作函数 70 void work(){ 71 //调用接口 72 mcpu->calculate(); 73 mvc->display(); 74 mme->storage(); 75 } 76 //提供析构函数释放3个电脑零件的堆区数据 77 ~Computer(){ 78 if (mcpu != NULL){ 79 delete mcpu; 80 mcpu = NULL; 81 } 82 if (mvc != NULL){ 83 delete mvc; 84 mvc = NULL; 85 } 86 if (mme != NULL){ 87 delete mme; 88 mme = NULL; 89 } 90 } 91 private: 92 CPU * mcpu; //CPU零件指针 93 VideoCard * mvc; //显卡零件指针 94 Memory * mme; //内存条零件指针 95 }; 96 //=======================================// 97 98 //组装测试机 99 void test01(){ 100 //法一 101 //第一台电脑的零件 102 CPU * ICPU1 = new ICPU; //堆区数据 指针ICPU1最后被传到电脑内的构造函数形参呢,被赋值到其成员属性上 103 VideoCard * ICard1 = new IVideoCard; //堆区数据 104 Memory * IMe1 = new IMemory; //堆区数据 105 106 //创建第一台电脑 107 Computer * c1 = new Computer(ICPU1, ICard1, IMe1); 108 c1->work(); 109 delete c1; 110 111 cout << endl; 112 //法二 113 //创建第二台电脑 114 Computer * c2 = new Computer(new ACPU, new AVideoCard, new AMemory); 115 c2->work(); 116 delete c2; 117 118 cout << endl; 119 //创建第三台电脑 120 Computer * c3 = new Computer(new ACPU, new IVideoCard, new IMemory); 121 c3->work(); 122 delete c3; 123 } 124 125 int main(){ 126 test01(); 127 system("pause"); 128 return 0; 129 }
标签:函数,对象,void,多态,N1,N2,public From: https://www.cnblogs.com/MorningMaple/p/16887685.html