当前位置:   article > 正文

c++学习笔记:多态

c++学习笔记:多态

4月1日

一、多态

1、多态分为两类:静态多态、动态多态

静态多态:函数的重载和运算符的重载属于静态多态,复用函数名

动态多态:派生类和虚函数运行时实现多态

区别:

静态多态的函数地址早绑定,编译阶段确定函数地址

动态多态的函数地址晚绑定,运行阶段确定函数地址

2、函数重写:函数返回值类型、函数名、参数列表完全相同

3、动态多态满足条件:

1)有继承关系

2)子类重写父类的虚函数

4、动态多态的使用

父类的指针或者引用指向子类的对象

5、多态案例—计算机类

1)多态的优点

代码组织清晰

可读性强

利于后期的扩展以及维护

2)案例

传统写法:

传统写法:如果想扩展新功能,需要修改源码,在真实的开发中,提倡开闭原则:对扩展进行开发,对修改进行关闭

多态写法:

代码:

  1. #include<iostream>
  2. using namespace std;
  3. //利用多态实现计算器
  4. //实现计算器抽象类
  5. class AbstractCalculator{
  6. public:
  7.     int m_num1;
  8.     int m_num2;
  9.     //设为虚函数
  10.     virtual int getResult()
  11.     {
  12.         return 0;
  13.     }
  14. };
  15. //加法计算器类
  16. class AddCalculator:public AbstractCalculator {
  17. public:
  18.     int getResult()
  19.     {
  20.         return AbstractCalculator::m_num1 +AbstractCalculator::m_num2;
  21.     }
  22.     int m_num1; //若此处再声明成员变量,则会造成此类的成员将父类的覆盖掉相加,而不能实现多态,或者加作用域也可以也可以
  23.     int m_num2;
  24. };
  25. //减法计算器类
  26. class SubCalculator :public AbstractCalculator {
  27. public:
  28.     int getResult()
  29.     {
  30.         return m_num1 - m_num2;
  31.     }
  32. };
  33. //乘法计算器类
  34. class MulCalculator :public AbstractCalculator {
  35. public:
  36.     int getResult()
  37.     {
  38.         return m_num1 * m_num2;
  39.     }
  40.    
  41. };
  42. //除法计算器类
  43. class ChuCalculator :public AbstractCalculator {
  44. public:
  45.     int getResult()
  46.     {
  47.         return m_num1 / m_num2;
  48.     }
  49.    
  50. };
  51. void test02()
  52. {
  53.     //多态使用方法:父类的指针或引用指向子类对象
  54.     AbstractCalculator *abc = new AddCalculator;
  55.     abc->m_num1 = 10;
  56.     abc->m_num2 = 5;
  57.     cout << abc->m_num1 << "+" << abc->m_num2 << "=" << abc->getResult() << endl; //注意子类中不能再重新声明m_num1,m_num2,否则会造成出错
  58.     //对象用完后,记得销毁,因为其置于堆区:需手动销毁
  59.     delete abc;
  60.     abc = new SubCalculator;
  61.     abc->m_num1 = 20;
  62.     abc->m_num2 = 15;
  63.     cout << abc->m_num1 << "-" << abc->m_num2 << "=" << abc->getResult() << endl;
  64.     delete abc;
  65.     abc = new MulCalculator;
  66.     abc->m_num1 = 10;
  67.     abc->m_num2 = 15;
  68.     cout << abc->m_num1 << "*" << abc->m_num2 << "=" << abc->getResult() << endl;
  69.     delete abc;
  70.     abc = new ChuCalculator;
  71.     abc->m_num1 = 200;
  72.     abc->m_num2 = 2;
  73.     cout << abc->m_num1 << "/" << abc->m_num2 << "=" << abc->getResult() << endl;
  74.     delete abc;
  75. }
  76. int main()
  77. {
  78.     test02();
  79.     return 0;
  80. }

6、纯虚函数和抽象类

在多态中,通常父类中的虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将其改为纯虚函数。

1)语法 virtual 返回值类型 函数名(参数列表)=0;

当类中有了纯虚函数,这个类也称为抽象类

2)抽象类的特点:

无法实例化对象;子类必须重写抽象类中的纯虚函数,否则也属于抽象类;

7、虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方法:将父类中的析构函数改为虚析构和纯虚析构

1)虚析构和纯虚析构的共性:

可以解决父类指针释放子类对象、都需要具体的函数实现

2)区别

如果是纯虚析构,该类属于抽象类,无法实例化对象

3)示例

常规不写虚析构的写法

  1. #include<iostream>
  2. using namespace std;
  3. #include<string>
  4. class Drink{
  5. public:
  6.     int m_num1;
  7.     int m_num2;
  8.     Drink()
  9.     {
  10.         cout << "Drink的构造函数调用" << endl;
  11.     }
  12.     //纯虚函数 此时类为抽象类
  13.     virtual void zhuWater() = 0;
  14.     void makeDrink()
  15.     {
  16.         zhuWater();
  17.     }
  18.     ~Drink()
  19.     {
  20.         cout << "Drink的析构函数调用" << endl;
  21.     }
  22. };
  23. //子类
  24. class Tea:public Drink {
  25. public:
  26.     Tea(string name)
  27.     {
  28.         cout << "Tea构造函数" << endl;
  29.         m_Name = new string(name);
  30.     }
  31.     virtual void zhuWater()
  32.     {
  33.         cout<<*m_Name << "煮水" << endl; //注意指针加*解引用
  34.     }
  35.     ~Tea()
  36.     {
  37.         if (m_Name != NULL)
  38.         {
  39.              cout << "Tea的析构函数调用" << endl;
  40.              delete m_Name;
  41.              m_Name = NULL;
  42.         }
  43.     }
  44.     string *m_Name;
  45.    
  46. };
  47. void test02()
  48. {
  49.     Drink *a = new Tea("龙井");
  50.     a->makeDrink();
  51.     delete a;
  52. }
  53. int main()
  54. {
  55.     test02();
  56.     return 0;
  57. }

结果:

可以看出并没有调用子类的析构来销毁在堆区建的属性数据。堆区数据没有释放干净会导致内存泄漏。

用虚析构解决:

纯虚析构(需要声明又需要有实现):

有了纯虚析构之后这个类也属于抽象类,无法实例化;

不是每个类都需要写虚析构或者纯虚析构,它们都是为了解决多态时,在子类堆区创建了数据,父类无法调用子类析构代码来释放堆区数据造成内存泄漏的问题。若子类堆区无数据,则可以不写它们。

8、案例:电脑组装

一台电脑分为CPU、内存条、显卡 分别写为三个抽象基类 并提供其功能的纯虚函数作为后续调用接口;

然后写不同的厂商类(生产不同的CPU、显卡、内存条),并实现其纯虚函数;

然后创建一个电脑类提供让电脑工作的函数,并且调用每个零件工作的接口;

即用不同厂商的零件去组装它,并使它工作;

代码:

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. //cpu类
  5. class CPU {
  6. //注意若不写明其访问权限 则默认为私有,类外不可访问
  7. public:
  8. //纯虚函数 计算
  9. virtual void calculate() = 0;
  10. };
  11. //显卡类
  12. class VideoCard {
  13. public:
  14. //纯虚函数 显示
  15. virtual void display() = 0;
  16. };
  17. //内存条类
  18. class StoreTiao {
  19. public:
  20. //纯虚函数 存储
  21. virtual void store() = 0;
  22. };
  23. //电脑类 提供工作的函数
  24. class Computer {
  25. public:
  26. //构造函数 传入详细零件指针
  27. Computer(CPU *cpu, VideoCard *videoCard, StoreTiao *memory)
  28. {
  29. m_cpu = cpu;
  30. m_videoCard = videoCard;
  31. m_memory = memory;
  32. }
  33. //让零件工作起来 调用其接口
  34. void doWork()
  35. {
  36. m_cpu->calculate();
  37. m_videoCard->display();
  38. m_memory->store();
  39. }
  40. //提供电脑析构函数 释放3个电脑零件
  41. ~Computer()
  42. {
  43. if (m_cpu != NULL)
  44. {
  45. delete m_cpu;
  46. m_cpu = NULL;
  47. }
  48. if (m_videoCard != NULL)
  49. {
  50. delete m_videoCard;
  51. m_videoCard = NULL;
  52. }
  53. if (m_memory != NULL)
  54. {
  55. delete m_memory;
  56. m_memory = NULL;
  57. }
  58. cout << "析构函数将零件内存释放完毕" << endl;
  59. }
  60. private:
  61. CPU *m_cpu;
  62. VideoCard *m_videoCard;
  63. StoreTiao *m_memory;
  64. };
  65. //具体厂商
  66. //戴尔
  67. class DelMemory : public StoreTiao {
  68. public:
  69. virtual void store()
  70. {
  71. cout << "戴尔内存条开始工作 存储" << endl;
  72. }
  73. };
  74. class DelCPU :public CPU {
  75. public:
  76. virtual void calculate()
  77. {
  78. cout << "戴尔CPU开始工作 计算" << endl;
  79. }
  80. };
  81. class DelVideoCard : public VideoCard{
  82. public:
  83. virtual void display()
  84. {
  85. cout << "戴尔显卡开始工作 显示" << endl;
  86. }
  87. };
  88. //Lenovo
  89. class LenovoMemory : public StoreTiao {
  90. public:
  91. virtual void store()
  92. {
  93. cout << "联想内存条开始工作 存储" << endl;
  94. }
  95. };
  96. class LenovoCPU :public CPU {
  97. public:
  98. virtual void calculate()
  99. {
  100. cout << "联想CPU开始工作 计算" << endl;
  101. }
  102. };
  103. class LenovoVideoCard : public VideoCard {
  104. public:
  105. virtual void display()
  106. {
  107. cout << "联想显卡开始工作 显示" << endl;
  108. }
  109. };
  110. //Intel
  111. class IntelMemory : public StoreTiao {
  112. public:
  113. virtual void store()
  114. {
  115. cout << "因特尔内存条开始工作 存储" << endl;
  116. }
  117. };
  118. class IntelCPU :public CPU {
  119. public:
  120. virtual void calculate()
  121. {
  122. cout << "因特尔CPU开始工作 计算" << endl;
  123. }
  124. };
  125. class IntelVideoCard : public VideoCard {
  126. public:
  127. virtual void display()
  128. {
  129. cout << "因特尔显卡开始工作 显示" << endl;
  130. }
  131. };
  132. void test01()
  133. {
  134. //第一台电脑零件
  135. //数据存放在堆区 需手动释放
  136. CPU *intelCpu = new IntelCPU;
  137. VideoCard *delVideoCard = new DelVideoCard;
  138. StoreTiao *lenovoMemore = new LenovoMemory;
  139. //创建第一台电脑
  140. //Computer computer1(intelCpu, delVideoCard, lenovoMemore);//第一种创建 放在栈区
  141. Computer *computer1 = new Computer(intelCpu, delVideoCard, lenovoMemore);//第二种创建 放在堆区 需手动释放
  142. computer1->doWork();
  143. delete computer1;
  144. }
  145. int main()
  146. {
  147. test01();
  148. return 0;
  149. }

注意释放内存!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/427624
推荐阅读
相关标签
  

闽ICP备14008679号