首页 > 其他分享 >类和对象——多态

类和对象——多态

时间:2022-11-14 18:56:01浏览次数:34  
标签:函数 对象 void 多态 N1 N2 public

多态:多种状态——一般说多态都指动态多态

多态的优点(体现于练手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

相关文章