ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++基础-7-多态

2022-05-03 15:31:26  阅读:122  来源: 互联网

标签:Num1 Num2 子类 void 基础 多态 C++ public


7. 多态

7.1 多态基本用法

 1 #include<iostream>
 2 using namespace std;
 3 
 4 // 多态
 5 
 6 // 动态多态满足条件:
 7 //    1.有继承关系
 8 //  2. 子类重写父类的虚函数
 9 // 
10 // 动态多态使用
11 // 父类的指针或者引用,指向子类对象
12 // 
13 
14 // 动物类
15 class Animal {
16 public:
17     // 虚函数,函数地址晚绑定
18     virtual    void speak() {
19         cout << "动物会说话" << endl;
20     }
21 };
22 
23 // 猫类
24 class Cat:public Animal {
25 public:
26     // 父类 虚函数 重写
27     // 重写:函数返回值类型、函数名、参数列表 完全相同
28     void speak() {
29         cout << "小猫在说话" << endl;
30 
31     }
32 };
33 
34 // 狗类
35 class Dog :public Animal {
36 public:
37     void speak() {
38         cout << "小狗在说话" << endl;
39 
40     }
41 };
42 
43 // 执行说话函数
44 // 地址早绑定,在编译阶段确定函数地址
45 // 如果想执行“猫说话”,那么这个函数地址就不能早绑定,需要在运行阶段进行绑定,即地址晚绑定
46 void doSpeak(Animal& animal) {  // 父类引用接收子类对象
47     // 通过对基类定义虚函数的方法
48     // 调用speak()函数时,函数地址根据传入的对象而动态绑定函数地址
49     animal.speak();
50 }
51 
52 void test01() {
53     Cat cat;
54     doSpeak(cat); // 父类引用接收子类对象
55 
56     Dog dog;
57     doSpeak(dog);
58 }
59 
60 int main() {
61 
62     test01();
63 
64     system("pause");
65 
66     return 0;
67 }
68 
69 // 总结
70 // 多态
71 // 分类:
72 //    静态多态:函数重载、运算符重载
73 //    动态多态:派生类、虚函数
74 //        静态多态与动态多态的区别:
75 //            静态:函数地址早绑定,编译阶段确定函数地址
76 //            动态:函数地址晚绑定,运行阶段确定函数地址
77 // 多态优点:
78 //    代码组织结构清晰
79 //    可读性强
80 //    有利于前期和后期的扩展以及维护
81 //  

 

7.2 多态案例(一) 计算器

  1 #include<iostream>
  2 #include<string>
  3 
  4 using namespace std;
  5 
  6 // 普通实现
  7 class Calculator {
  8 public:
  9     int getResult(string oper) {
 10         if (oper == "+") {
 11             return m_Num1 + m_Num2;
 12         }
 13         else if (oper == "-")
 14         {
 15             return m_Num1 - m_Num2;
 16         }
 17         else if (oper == "*") {
 18             return m_Num1 * m_Num2;
 19         }
 20     }
 21 
 22     int m_Num1; // 操作数1
 23     int m_Num2; // 操作数2
 24 };
 25 
 26 // 利用多态实现计算器
 27 
 28 // 实现计算器抽象类
 29 class AbstractCalculater {
 30 public:
 31 
 32     virtual int getResult() {
 33         return 0;
 34 
 35     }
 36     int m_Num1;
 37     int m_Num2;
 38 };
 39 
 40 // 加法计算器类
 41 class AddCalculator : public AbstractCalculater {
 42     int getResult() {
 43         return m_Num1 + m_Num2;
 44     }
 45 };
 46 
 47 // 减法计算器类
 48 class SubCalculator : public AbstractCalculater {
 49     int getResult() {
 50         return m_Num1 - m_Num2;
 51     }
 52 };
 53 
 54 // 乘法计算器类
 55 class MulCalculator : public AbstractCalculater {
 56     int getResult() {
 57         return m_Num1 * m_Num2;
 58     }
 59 };
 60 
 61 
 62 void test01() {
 63 
 64     // 普通实现
 65     Calculator c;
 66     c.m_Num1 = 20;
 67     c.m_Num2 = 10;
 68 
 69     cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
 70     cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
 71     cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
 72 }
 73 
 74 void test02() {
 75     // 多态使用条件
 76     // 父类指针或者引用指向子类对象
 77 
 78     // 加法运算
 79     AbstractCalculater* abc = new AddCalculator;
 80     abc->m_Num1 = 20;
 81     abc->m_Num2 = 10;
 82     cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
 83     // 堆区数据,用完后记得销毁
 84     delete abc;
 85 
 86     // 减法运算
 87     abc = new SubCalculator;
 88     // 上面的delete abc只是释放了堆区数据,abc依旧是个父类的指针
 89     abc->m_Num1 = 20;
 90     abc->m_Num2 = 10;
 91     cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
 92     delete abc;
 93 
 94     // 相乘运算
 95     abc = new MulCalculator;
 96     abc->m_Num1 = 20;
 97     abc->m_Num2 = 10;
 98     cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
 99     delete abc;
100 
101 }
102 
103 int main() {
104 
105     //test01();
106 
107     test02();
108 
109     system("pause");
110 
111     return 0;
112 
113 }
114 
115 // 总结
116 // 在真正的开发中,提倡 开闭原则
117 // 开闭原则: 对扩展进行开放,对修改进行关闭
118 // 

 

7.3 纯虚函数和抽象类

 1 #include<iostream>
 2 using namespace std;
 3 
 4 // 纯虚函数与抽象类
 5 
 6 class Base {
 7 public:
 8     // 纯虚函数
 9     // 该类为抽象类
10     virtual void func() = 0;
11 };
12 
13 class Son :public Base {
14 public:
15     void func() {
16         cout << "子类重写了父类的纯虚函数!" << endl;
17     }
18 };
19 
20 void test01() {
21     //Base b;  // 报错,抽象类,无法实例化对象
22     //new Base;  // 报错,堆区也无法实例化
23 
24     //Son s;  // 报错,子类需要重写抽象类(父类)的纯虚函数
25 
26     Son s;
27     s.func();
28 }
29 
30 int main() {
31 
32     test01();
33 
34     system("pause");
35 
36     return 0;
37 }
38 
39 
40 // 总结
41 // 在多态中,通常父类中的虚函数的实现是毫无意义的,主要都是调用子类重写的内容
42 // 因此可以将虚函数改为传虚函数
43 // 
44 // 纯虚函数语法: virtual 返回值类型 函数名 (参数列表) = 0;
45 // 
46 // 当类中有了纯虚函数,这类也成为抽象类
47 // 
48 // 抽象类特点:
49 //        无法实例化对象
50 //        子类必须重写抽象类中的纯虚函数,否则也属于抽象类
51 // 
52 // 

 

7.4 多态案例(二) 制作饮品

  1 #include<iostream>
  2 #include<string>
  3 
  4 using namespace std;
  5 
  6 // 多态案例2 制作饮品
  7 class AbstractDrinking {
  8 public:
  9     // 煮水
 10     virtual void Boil() = 0;
 11 
 12     // 冲泡
 13     virtual void Brew() = 0;
 14 
 15     // 倒入水中
 16     virtual void PourInCup() = 0;
 17 
 18     // 加入辅料
 19     virtual void PutSomething() = 0;
 20 
 21     // 制作饮品
 22     void makeDrink() {
 23         Boil();
 24         Brew();
 25         PourInCup();
 26         PutSomething();
 27     }
 28 
 29 };
 30 
 31 // 制作咖啡
 32 class Coffee : public AbstractDrinking {
 33 public:
 34     // 煮水
 35     virtual void Boil() {
 36         cout << "煮农夫山泉" << endl;
 37     }
 38 
 39     // 冲泡
 40     virtual void Brew() {
 41         cout << "冲泡咖啡" << endl;
 42     }
 43 
 44     // 倒入杯中
 45     virtual void PourInCup() {
 46         cout << "倒入杯中" << endl;
 47     }
 48 
 49     // 加入辅料
 50     virtual void PutSomething() {
 51         cout << "加入糖和牛奶" << endl;
 52     }
 53 };
 54 
 55 // 制作奶茶
 56 class MilkTea : public AbstractDrinking {
 57 public:
 58     // 煮水
 59     virtual void Boil() {
 60         cout << "煮农百岁山" << endl;
 61     }
 62 
 63     // 冲泡
 64     virtual void Brew() {
 65         cout << "冲泡珍珠" << endl;
 66     }
 67 
 68     // 倒入杯中
 69     virtual void PourInCup() {
 70         cout << "倒入杯中" << endl;
 71     }
 72 
 73     // 加入辅料
 74     virtual void PutSomething() {
 75         cout << "加入珍珠、果粒、椰蓉" << endl;
 76     }
 77 };
 78 
 79 void doWork(AbstractDrinking* abs) {
 80     abs->makeDrink();
 81     delete abs;
 82 }
 83 
 84 void test01() {
 85     // 制作咖啡
 86     cout << "制作咖啡" << endl;
 87     doWork(new Coffee);
 88 
 89     cout << "\n制作奶茶" << endl;
 90     doWork(new MilkTea);
 91 }
 92 
 93 int main() {
 94 
 95     test01();
 96 
 97     system("pause");
 98 
 99     return 0;
100 }

 

7.5 虚析构与纯虚析构

 1 #include<iostream>
 2 #include<string>
 3 using namespace std;
 4 
 5 
 6 class Animal {
 7 public:
 8     virtual    void speak() = 0;
 9     Animal() {
10         cout << "Animal的构造函数" << endl;
11     }
12     // 虚析构
13     //virtual ~Animal() {
14     //    cout << "Animal的析构函数" << endl;
15     //}
16 
17     // 纯虚析构  需要声明,也需要函数实现
18     virtual ~Animal() = 0;
19     // 有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
20 
21 };
22 
23 Animal::~Animal() {
24     cout << "Animal的纯析构函数" << endl;
25 }
26 
27 class Cat :public Animal {
28 public:
29     Cat(string name) {
30         cout << "Cat的构造函数" << endl;
31         m_Name = new string(name);
32     }
33     void speak() {
34         cout << *m_Name << "小猫在说话" << endl;
35     }
36 
37     string* m_Name;
38 
39     ~Cat() {
40         cout << "Cat的析构函数" << endl;
41         if (m_Name != NULL) {
42             delete m_Name;
43             m_Name = NULL;
44         }
45     }
46 };
47 
48 void test01() {
49     Animal* animal = new Cat("Tom");
50     animal->speak();
51     delete animal;
52     // 父类指针在析构的时候,不会调用子类中的析构函数,
53     // 导致子类如果存在堆区数据,会出现内存泄露情况
54     // 解决方法:父类改为虚析构
55     // 这样就会释放子类的内存(走子类的析构函数)
56 
57 
58 
59 }
60 
61 int main() {
62 
63     test01();
64 
65     system("pause");
66 
67     return 0;
68 }
69 
70 // 总结
71 // 虚析构与纯虚析构
72 // 多态使用时,如果子类中有属性开辟到堆区,那么父类指针析构函数无法调用到子类的析构代码
73 // 解决方案:将父类的析构函数改为虚析构或者纯虚析构
74 // 
75 // 虚析构与纯虚析构共性:
76 //    可以解决父类指针释放子类对象
77 //    都需要具体的函数实现
78 // 
79 // 虚析构与纯虚析构区别:
80 //    如果是纯虚析构,该类属于抽象类,无法实例化对象
81 // 
82 // 虚析构:virtual ~类名(){}
83 // 
84 // 纯虚析构: 
85 //        virtual ~类名() = 0;
86 //        // 在类外需要初始化
87 //        类名::~类名(){}
88 // 
89 // 其他结论
90 //    1.虚析构和纯虚析构就是用来解决父类指针释放子类对象的
91 //    2.如果子类中没有堆区数据,也可以不写虚析构或纯虚析构
92 //    3.拥有纯虚析构的类也属于抽象类,无法实例化对象
93 // 

 

参考:《黑马程序员》C++教程

标签:Num1,Num2,子类,void,基础,多态,C++,public
来源: https://www.cnblogs.com/LYH-win/p/16218477.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有