• 多态(个人学习笔记黑马学习)


    多态分为两类

    • 静态多态: 函数重载和 运算符重载属于静态多态,复用函数名
    • 动态多态: 派生类和虚图数实现运行时多态

    静态多态和动态多态区别:

    • 静态多态的函数地址早绑定 · 编译阶段确定函数地址
    • 动态多态的函数地址晚绑定 · 运行阶段确定函数地址

    1、基本语法

    1. #include
    2. using namespace std;
    3. #include
    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. //重写 函数返回值类型 函数名 参数列表 完全相同
    16. void speak() {
    17. cout << "小猫在说话" << endl;
    18. }
    19. };
    20. //狗类
    21. class Dog :public Animal {
    22. public:
    23. void speak() {
    24. cout << "小狗在说话" << endl;
    25. }
    26. };
    27. //执行说话的函数
    28. //地址早绑定 在编译阶段确定函数地址
    29. //如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
    30. //动态多态满足条件
    31. //1、有继承关系
    32. //2、子类重写父类的虚函数
    33. //动态多态的使用
    34. //父类的指针或者引用 指向子类对象
    35. void doSpeak(Animal& animal) { //Animal& animal = cat;
    36. animal.speak();
    37. }
    38. void test01() {
    39. Cat cat;
    40. doSpeak(cat);
    41. Dog dog;
    42. doSpeak(dog);
    43. }
    44. int main() {
    45. test01();
    46. system("pause");
    47. return 0;
    48. }

    2、案例1:计算器

    案例描述:
    分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

    1. #include
    2. using namespace std;
    3. #include
    4. //普通写法
    5. class Calculator {
    6. public:
    7. int getResult(string oper) {
    8. if (oper == "+") {
    9. return m_Num1 + m_Num2;
    10. }
    11. else if (oper == "-") {
    12. return m_Num1 - m_Num2;
    13. }
    14. else if (oper == "*") {
    15. return m_Num1 * m_Num2;
    16. }
    17. //如果想扩展新的功能,需要修改源码
    18. //在真正的开发中 提倡 开闭原则
    19. //开闭原则:对扩展进行开发,对修改进行关闭
    20. }
    21. int m_Num1;//操作数1
    22. int m_Num2;//操作数2
    23. };
    24. void test01() {
    25. //创建计算器对象
    26. Calculator c;
    27. c.m_Num1 = 10;
    28. c.m_Num2 = 10;
    29. cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
    30. cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
    31. cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
    32. }
    33. //利用多态实现计算器
    34. //多态好处:
    35. // 1、组织结构清晰
    36. // 2、可读性强
    37. // 3、对前期和后期拓展和维护性高
    38. //实现计算器抽象类
    39. class AbstractCalculator {
    40. public:
    41. virtual int getResult() {
    42. return 0;
    43. }
    44. int m_Num1;
    45. int m_Num2;
    46. };
    47. //加法计算器类
    48. class AddCalculator :public AbstractCalculator {
    49. public:
    50. int getResult() {
    51. return m_Num1 + m_Num2;
    52. }
    53. };
    54. //减法计算器类
    55. class SubCalculator :public AbstractCalculator {
    56. public:
    57. int getResult() {
    58. return m_Num1 - m_Num2;
    59. }
    60. };
    61. //乘法计算器类
    62. class MulCalculator :public AbstractCalculator {
    63. public:
    64. int getResult() {
    65. return m_Num1 * m_Num2;
    66. }
    67. };
    68. void test02() {
    69. //多态使用
    70. //父类指针或者引用指向子类对象
    71. //
    72. //加法运算
    73. AbstractCalculator* abc = new AddCalculator;
    74. abc->m_Num1 = 100;
    75. abc->m_Num2 = 100;
    76. cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
    77. //用完后记得销毁
    78. delete abc;
    79. abc = NULL;
    80. //减法运算
    81. abc = new SubCalculator;
    82. abc->m_Num1 = 100;
    83. abc->m_Num2 = 100;
    84. cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
    85. delete abc;
    86. abc = NULL;
    87. //乘法运算
    88. abc = new MulCalculator;
    89. abc->m_Num1 = 100;
    90. abc->m_Num2 = 100;
    91. cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
    92. delete abc;
    93. abc = NULL;
    94. }
    95. int main() {
    96. //test01();
    97. test02();
    98. system("pause");
    99. return 0;
    100. }


    3、纯虚函数和抽象类

    1. #include
    2. using namespace std;
    3. #include
    4. class Base {
    5. public:
    6. //纯虚函数
    7. //只要有一个纯虚函数,这个类称为抽象类
    8. //抽象类特点:
    9. //1、无法实例化对象
    10. //2、抽象类的子类 必须要重写父类中的纯虚函数,否则也属于抽象类
    11. virtual void func() = 0;
    12. };
    13. class Son :public Base {
    14. public:
    15. virtual void func() {
    16. cout << "fun()函数调用" << endl;
    17. }
    18. };
    19. void test01() {
    20. //Base b;//抽象类无法实例化对象
    21. //new Base;//抽象类无法实例化对象
    22. //Son s;//子类必须重写父类中的纯虚函数,否则无法实例化对象
    23. Base* base = new Son;
    24. base->func();
    25. }
    26. int main() {
    27. system("pause");
    28. return 0;
    29. }

    4、案例2:制作饮品

    案例描述:
    制作饮品的大致流程为: 煮水 冲泡 倒入杯中 加入辅料
    利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

    1. #include
    2. using namespace std;
    3. #include
    4. class AbstractDrinking {
    5. public:
    6. //煮水
    7. virtual void Boil() = 0;
    8. //冲泡
    9. virtual void Brew() = 0;
    10. //倒入杯中
    11. virtual void PourInCup() = 0;
    12. //加入辅料
    13. virtual void PutSomething() = 0;
    14. //制作饮品
    15. void makeDrink() {
    16. Boil();
    17. Brew();
    18. PourInCup();
    19. PutSomething();
    20. }
    21. };
    22. //制作咖啡
    23. class Coffee:public AbstractDrinking {
    24. public:
    25. //煮水
    26. virtual void Boil() {
    27. cout << "煮农夫山泉" << endl;
    28. };
    29. //冲泡
    30. virtual void Brew() {
    31. cout << "冲泡咖啡" << endl;
    32. };
    33. //倒入杯中
    34. virtual void PourInCup() {
    35. cout << "倒入杯中" << endl;
    36. };
    37. //加入辅料
    38. virtual void PutSomething() {
    39. cout << "加入糖和牛奶" << endl;
    40. };
    41. };
    42. //制作茶叶
    43. class Tea :public AbstractDrinking {
    44. public:
    45. //煮水
    46. virtual void Boil() {
    47. cout << "煮矿泉水" << endl;
    48. };
    49. //冲泡
    50. virtual void Brew() {
    51. cout << "冲泡茶叶" << endl;
    52. };
    53. //倒入杯中
    54. virtual void PourInCup() {
    55. cout << "倒入杯中" << endl;
    56. };
    57. //加入辅料
    58. virtual void PutSomething() {
    59. cout << "加入枸杞" << endl;
    60. };
    61. };
    62. //制作函数
    63. void doWork(AbstractDrinking* abs) {//AbstractDrinking* abs=new Coffee;
    64. abs->makeDrink();
    65. delete abs;//释放
    66. }
    67. void test01() {
    68. //制作咖啡
    69. doWork(new Coffee);
    70. cout << "---------------" << endl;
    71. //制作茶叶
    72. doWork(new Tea);
    73. }
    74. int main() {
    75. test01();
    76. system("pause");
    77. return 0;
    78. }


    5、虚析构和纯虚析构 

    虚析构和纯虚析构共性

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

    虚析构和纯虚析构区别:

    • 如果是纯虚析构,该类属于抽象类,无法实例化对象
    1. #include
    2. using namespace std;
    3. #include
    4. class Animal {
    5. public:
    6. Animal() {
    7. cout << "Animal构造函数调用" << endl;
    8. }
    9. //利用虚析构可以解决 父类指针释放子类对象时不干净的问题
    10. /*virtual ~Animal()
    11. {
    12. cout<< "Animal虚析构函数调用" << endl;
    13. }*/
    14. //纯虚析构 需要声明也需要实现
    15. virtual ~Animal() = 0;
    16. //纯虚函数
    17. virtual void speak() = 0;
    18. };
    19. Animal:: ~Animal() {
    20. cout << "Animal纯虚析构函数调用" << endl;
    21. }
    22. class Cat :public Animal {
    23. public:
    24. Cat(string name) {
    25. cout << "Cat构造函数调用" << endl;
    26. m_Name= new string(name);
    27. }
    28. virtual void speak() {
    29. cout << +m_Name<<"小猫在说话" << endl;
    30. }
    31. ~Cat() {
    32. if (m_Name != NULL) {
    33. cout << "Cat析构函数调用" << endl;
    34. delete m_Name;
    35. m_Name = NULL;
    36. }
    37. }
    38. string* m_Name;
    39. };
    40. void test01() {
    41. Animal* animal = new Cat("Tom");
    42. animal->speak();
    43. //父类指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄露
    44. delete animal;
    45. }
    46. int main() {
    47. test01();
    48. system("pause");
    49. return 0;
    50. }

    6、案例三:电脑组装

    • 电脑主要组成部件为 CPU (用于计算),显卡 (用于显示),内存条 (用于存储)
    • 将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
    • 创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
    • 测试时组装三台不同的电脑进行工作
    1. #include
    2. using namespace std;
    3. #include
    4. //抽象不同零件类
    5. //抽象CPU类
    6. class CPU {
    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 Memory {
    19. public:
    20. //抽象的存储函数
    21. virtual void storage() = 0;
    22. };
    23. //电脑类
    24. class Computer {
    25. public:
    26. Computer(CPU* cpu, VideoCard* vc, Memory* mem) {
    27. m_cpu = cpu;
    28. m_vc = vc;
    29. m_mem = mem;
    30. }
    31. //提供工作的函数
    32. void work() {
    33. //让零件工作起来,调用接口
    34. m_cpu->calculate();
    35. m_vc->display();
    36. m_mem->storage();
    37. }
    38. //提供析构函数 释放3个电脑零件
    39. ~Computer() {
    40. //释放cpu零件
    41. if (m_cpu != NULL) {
    42. delete m_cpu;
    43. m_cpu = NULL;
    44. }
    45. //释放显卡零件
    46. if (m_vc != NULL) {
    47. delete m_vc;
    48. m_vc = NULL;
    49. }
    50. //释放内存条零件
    51. if (m_mem != NULL) {
    52. delete m_mem;
    53. m_mem = NULL;
    54. }
    55. }
    56. private:
    57. CPU* m_cpu;//CPU的零件指针
    58. VideoCard* m_vc;//显卡零件指针
    59. Memory* m_mem;//内存条零件指针
    60. };
    61. //具体厂商
    62. //Intel厂商
    63. class IntelCPU :public CPU {
    64. public:
    65. virtual void calculate() {
    66. cout << "Intel的CPU开始计算了" << endl;
    67. }
    68. };
    69. class IntelVideoCard :public VideoCard {
    70. public:
    71. virtual void display() {
    72. cout << "Intel的显卡开始显示了" << endl;
    73. }
    74. };
    75. class IntelMemory :public Memory {
    76. public:
    77. virtual void storage() {
    78. cout << "Intel的内存条开始存储了" << endl;
    79. }
    80. };
    81. //Lenvo厂商
    82. class LenvoCPU :public CPU {
    83. public:
    84. virtual void calculate() {
    85. cout << "Lenvo的CPU开始计算了" << endl;
    86. }
    87. };
    88. class LenvoVideoCard :public VideoCard {
    89. public:
    90. virtual void display() {
    91. cout << "Lenvo的显卡开始显示了" << endl;
    92. }
    93. };
    94. class LenvoMemory :public Memory {
    95. public:
    96. virtual void storage() {
    97. cout << "Lenvo的内存条开始存储了" << endl;
    98. }
    99. };
    100. void test01() {
    101. //第一台电脑零件
    102. CPU* intelCpu = new IntelCPU;
    103. VideoCard* intelCard = new IntelVideoCard;
    104. Memory* intelMem = new IntelMemory;
    105. cout << "第一台电脑开始工作" << endl;
    106. //创建第一台电脑
    107. Computer* computer1 = new Computer(intelCpu, intelCard, intelMem);
    108. computer1->work();
    109. delete computer1;
    110. cout << "---------------------" << endl;
    111. cout << "第二台电脑开始工作" << endl;
    112. //创建第二台电脑
    113. Computer* computer2 = new Computer(new LenvoCPU,new LenvoVideoCard,new LenvoMemory);
    114. computer2->work();
    115. delete computer2;
    116. cout << "---------------------" << endl;
    117. cout << "第三台电脑开始工作" << endl;
    118. //创建第三台电脑
    119. Computer* computer3 = new Computer(new LenvoCPU, new IntelVideoCard, new LenvoMemory);
    120. computer3->work();
    121. delete computer3;
    122. }
    123. int main() {
    124. test01();
    125. system("pause");
    126. return 0;
    127. }

  • 相关阅读:
    C#中错误与异常处理
    使用Spring Boot和MyBatis访问数据库
    华为配置wlan
    编写一个代码将一个带头节点的单链表A分解为两个带头结点的A和B,使得A表中含有原表中序号为奇数的元素,B表中含有序号为偶数的元素,保持相对位置不变
    自定义表单开源好用吗?有哪些优势特点?
    学生如何利用假期提升个人能力?
    PCL 点云超体素分割-SupervoxelClustering
    提升媒体文字质量:常见错误及改进措施解析
    postgresql数据库docker
    期货开户手续费加一分是主流
  • 原文地址:https://blog.csdn.net/m0_59848857/article/details/132697791