• 浅谈C++|多态篇



    1.多态的基本概念

    多态是C++面向对象三大特性之一多态分为两类
    1. 静态多态:函数重载和运算符重载属于静态多态,复用函数名·

    2.动态多态:派生类和虚函数实现运行时多态
    静态多态和动态多态区别:
    ·静态多态的函数地址早绑定–编译阶段确定函数地址

    ·动态多态的函数地址晚绑定–运行阶段确定函数地址下面通过案例进行讲解多态

    动态多态满足条件

    1、有继承关系
    2、子类重写父类的虚函数

    动态多态使用
    父类的指针或者引用执行子类对象

    重写:函数返回值类型函数名参数列表完全—致称为重写

     静态多态代码:

    1. #include
    2. using namespace std;
    3. class dongwu {
    4. public:
    5. void speak() {
    6. cout << "动物叫" << endl;
    7. }
    8. };
    9. class cat :public dongwu {
    10. void speak() {
    11. cout << "猫叫" << endl;
    12. }
    13. };
    14. //早绑定,编译阶段就确定函数的地址
    15. void speak(dongwu& p) {
    16. p.speak();
    17. }
    18. void fun() {
    19. cat p;
    20. speak(p);
    21. }
    22. int main() {
    23. fun();
    24. cat p;
    25. //发生隐式转换,只能把儿子转为父亲,退化
    26. dongwu m = p;
    27. m.speak();
    28. return 0;
    29. }

    注意转化,以及早绑定的特性是执行当前的类型的函数,不执行儿子的函数

    动态动态代码: 

    1. #include
    2. using namespace std;
    3. class dongwu {
    4. public:
    5. //virtual虚函数关键字
    6. virtual void speak() {
    7. cout << "动物叫" << endl;
    8. }
    9. };
    10. class cat :public dongwu {
    11. //子类的virtual可加可不加
    12. virtual void speak() {
    13. cout << "猫叫" << endl;
    14. }
    15. };
    16. //晚绑定,执行子类的函数
    17. void speak(dongwu& p) {
    18. p.speak();
    19. }
    20. void fun() {
    21. cat p;
    22. speak(p);
    23. }
    24. int main() {
    25. fun();
    26. return 0;
    27. }

    二.多态的底层原理

    我们先看以下代码:

    1. #include
    2. using namespace std;
    3. class father1 {
    4. public:
    5. void speak() {
    6. cout << "动物叫" << endl;
    7. }
    8. };
    9. class father2 {
    10. public:
    11. virtual void speak() {
    12. cout << "动物叫" << endl;
    13. }
    14. };
    15. int main() {
    16. cout << sizeof(father1) << endl;
    17. cout << sizeof(father2) << endl;
    18. return 0;
    19. }

     当类的成员函数加入虚函数关键字后,会发现类的大小发生了改变。此时类的内部结构。此时类的内部会多一个指针虚函数(表)指针,虚函数指针指向虚函数表,虚函数表中存储虚函数的入口地址

    那么当派生类继承基类后,如果成员函数没有重名,那么会完全继承父类的结构。 

     

     但是当派生类,重写基函数的虚函数时,派生类中的虚函数表会发生改变,此时虚函数表指向派生类的虚函数,基类的虚函数被覆盖。

     

     此时,我们有派生类隐式转换为基类时,虚函数表中的内容并不改变,此时调用虚函数,执行的是派生类的虚函数。

    三.多态的优点 

    1、组织结构清晰

    2、可读性强

    3、对于前期和后期扩展以及维护性高

     普通计算机类:

    1. #include
    2. using namespace std;
    3. class jisuanqi {
    4. public:
    5. int a, b;
    6. int jisuan(string fu) {
    7. if (fu == "+") {
    8. return a + b;
    9. }
    10. else if (fu == "-") {
    11. return a - b;
    12. }
    13. else if (fu == "*") {
    14. return a * b;
    15. }
    16. }
    17. };
    18. void fun() {
    19. jisuanqi q;
    20. q.a = 200;
    21. q.b = 100;
    22. cout << q.a << " - " << q.b << " = " << q.jisuan("-") << endl;
    23. cout << q.a << " + " << q.b << " = " << q.jisuan("+") << endl;
    24. cout << q.a << " * " << q.b << " = " << q.jisuan("*") << endl;
    25. }
    26. int main() {
    27. fun();
    28. return 0;
    29. }

     多态计算机类:

    1. #include
    2. using namespace std;
    3. class jisuanqi {
    4. public:
    5. int a;
    6. int b;
    7. virtual int jisuan() {
    8. return 0;
    9. }
    10. };
    11. class add :public jisuanqi {
    12. virtual int jisuan() {
    13. return a+b;
    14. }
    15. };
    16. class jian :public jisuanqi {
    17. virtual int jisuan() {
    18. return a - b;
    19. }
    20. };
    21. class cheng:public jisuanqi {
    22. virtual int jisuan() {
    23. return a * b;
    24. }
    25. };
    26. void fun() {
    27. jisuanqi* p = new add;
    28. p->a = 200;
    29. p->b = 100;
    30. cout << p->a << " + " << p->b << " = " << p->jisuan()<
    31. delete p;
    32. p = new jian;
    33. p->a = 200;
    34. p->b = 100;
    35. cout << p->a << " - " << p->b << " = " << p->jisuan()<
    36. delete p;
    37. p = new cheng;
    38. p->a = 200;
    39. p->b = 100;
    40. cout << p->a << " * " << p->b << " = " << p->jisuan()<
    41. delete p;
    42. }
    43. int main() {
    44. fun();
    45. return 0;
    46. }

    四.纯虚函数和抽象类

    在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容因此可以将虚函数改为纯虚函数。当类中有了纯虚函数,这个类也称为抽象类

    纯虚函数语法: virtual    返回值类型   函数名︰(参数列表)= 0 ;

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

     代码:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. //纯虚函数
    6. virtual void fun() = 0;
    7. };
    8. class son :public father{
    9. public:
    10. void fun() {
    11. cout << "我是sond" << endl;
    12. }
    13. };
    14. void fun() {
    15. //多态f必须是指针或者引用
    16. //father f; 报错不可实例化
    17. father* f = new son;
    18. f->fun();
    19. }
    20. int main() {
    21. fun();
    22. return 0;
    23. }

    案例制作饮品:

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. virtual void zhushui() = 0;
    6. virtual void chongpao() = 0;
    7. virtual void daoru() = 0;
    8. virtual void jialiao() = 0;
    9. void fun() {
    10. zhushui();
    11. chongpao();
    12. daoru();
    13. jialiao();
    14. }
    15. };
    16. class tea :public father{
    17. void zhushui() {
    18. cout << "煮山泉水" << endl;
    19. };
    20. void chongpao() {
    21. cout << "冲茶" << endl;
    22. };
    23. void daoru() {
    24. cout << "倒入茶杯中" << endl;
    25. };
    26. void jialiao() {
    27. cout << "加入枸杞" << endl;
    28. };
    29. };
    30. class kafei : public father{
    31. void zhushui() {
    32. cout << "煮水" << endl;
    33. };
    34. void chongpao() {
    35. cout << "冲咖啡" << endl;
    36. };
    37. void daoru() {
    38. cout << "倒入咖啡杯中" << endl;
    39. };
    40. void jialiao() {
    41. cout << "加入奶和糖" << endl;
    42. };
    43. };
    44. //函数接口
    45. void fun(father* p) {
    46. p->fun();
    47. delete p;
    48. }
    49. int main() {
    50. fun(new tea);
    51. cout << "----------" << endl;
    52. fun(new kafei);
    53. return 0;
    54. }

    五. 虚析构和纯虚析构

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

    将父类中的析构函数改为虚析构或者纯虚析构


    虚析构和纯虚析构共性:
    ·可以解决父类指针释放子类对象·都需要有具体的函数实现


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

    代码: 

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. //纯虚函数
    6. virtual void fun() =0;
    7. father() {
    8. cout << "father构造函数" << endl;
    9. }
    10. ~father() {
    11. cout << "father析构函数" << endl;
    12. }
    13. };
    14. class son :public father {
    15. public:
    16. //堆区开辟数据
    17. son(int age) {
    18. cout << "son构造函数" << endl;
    19. this->age = new int(age);
    20. }
    21. ~son() {
    22. cout << "son析构函数" << endl;
    23. if (this->age != NULL) {
    24. delete age;
    25. age = NULL;
    26. }
    27. }
    28. void fun() {
    29. cout << *age<< "son的fun函数调用" << endl;
    30. }
    31. int* age;
    32. };
    33. void fun() {
    34. father* p = new son(21);
    35. delete p;
    36. }
    37. int main() {
    38. fun();
    39. return 0;
    40. }

    如图,当发生多态时,基类并不会调用子类的析构函数,当子类中含有堆区开辟的空间时。会造成内存泄漏。此时需要虚析构或纯虚析构来解决。

    虚析构代码: 

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. //纯虚函数
    6. virtual void fun() =0;
    7. father() {
    8. cout << "father构造函数" << endl;
    9. }
    10. virtual ~father() {
    11. cout << "father析构函数" << endl;
    12. }
    13. };
    14. class son :public father {
    15. public:
    16. //堆区开辟数据
    17. son(int age) {
    18. cout << "son构造函数" << endl;
    19. this->age = new int(age);
    20. }
    21. ~son() {
    22. cout << "son析构函数" << endl;
    23. if (this->age != NULL) {
    24. delete age;
    25. age = NULL;
    26. }
    27. }
    28. void fun() {
    29. cout << *age<< "son的fun函数调用" << endl;
    30. }
    31. int* age;
    32. };
    33. void fun() {
    34. father* p = new son(21);
    35. delete p;
    36. }
    37. int main() {
    38. fun();
    39. return 0;
    40. }

    纯虚析构

    1. #include
    2. using namespace std;
    3. class father {
    4. public:
    5. //纯虚函数
    6. virtual void fun() =0;
    7. father() {
    8. cout << "father构造函数" << endl;
    9. }
    10. virtual ~father() = 0;
    11. };
    12. //纯虚函数必须
    13. father::~father()
    14. {
    15. cout << "father析构函数" << endl;
    16. }
    17. class son :public father {
    18. public:
    19. //堆区开辟数据
    20. son(int age) {
    21. cout << "son构造函数" << endl;
    22. this->age = new int(age);
    23. }
    24. ~son() {
    25. cout << "son析构函数" << endl;
    26. if (this->age != NULL) {
    27. delete age;
    28. age = NULL;
    29. }
    30. }
    31. void fun() {
    32. cout << *age<< "son的fun函数调用" << endl;
    33. }
    34. int* age;
    35. };
    36. void fun() {
    37. father* p = new son(21);
    38. delete p;
    39. }
    40. int main() {
    41. fun();
    42. return 0;
    43. }

    案例计算机

    1. #include
    2. using namespace std;
    3. class CPU {
    4. public:
    5. //纯虚函数
    6. virtual void func() = 0;
    7. };
    8. class Memory_Module {
    9. public:
    10. //纯虚函数
    11. virtual void func() = 0;
    12. };
    13. class Graphics_card {
    14. public:
    15. //纯虚函数
    16. virtual void func() = 0;
    17. };
    18. class CPU_intel : public CPU {
    19. public:
    20. void func() {
    21. cout << "intel的CPU工作" << endl;
    22. }
    23. };
    24. class Graphics_card_intel : public Graphics_card {
    25. public:
    26. void func() {
    27. cout << "intel的显卡工作" << endl;
    28. }
    29. };
    30. class Memory_Module_intel : public Memory_Module {
    31. public:
    32. void func() {
    33. cout << "intel的内存条工作" << endl;
    34. }
    35. };
    36. class CPU_lenovo: public CPU {
    37. public:
    38. void func() {
    39. cout << "联想的CPU工作" << endl;
    40. }
    41. };
    42. class Graphics_card_lenovo : public Graphics_card {
    43. public:
    44. void func() {
    45. cout << "联想的显卡工作" << endl;
    46. }
    47. };
    48. class Memory_Module_lenovo : public Memory_Module {
    49. public:
    50. void func() {
    51. cout << "联想的内存条工作" << endl;
    52. }
    53. };
    54. class computer {
    55. public:
    56. //当传入的是子类时发生多态
    57. computer() {};
    58. computer(CPU* CPU , Memory_Module* m, Graphics_card* g) {
    59. this->cpu = CPU;
    60. this->m = m;
    61. this->g = g;
    62. }
    63. void work() {
    64. cpu->func();
    65. m->func();
    66. g->func();
    67. }
    68. private:
    69. CPU* cpu;
    70. Memory_Module* m;
    71. Graphics_card* g;
    72. };
    73. void fun() {
    74. CPU_lenovo* c1 = new CPU_lenovo;
    75. CPU_intel* c2 = new CPU_intel;
    76. Graphics_card_intel* g1 = new Graphics_card_intel;
    77. Graphics_card_lenovo* g2 = new Graphics_card_lenovo;
    78. Memory_Module_intel* m1 = new Memory_Module_intel;
    79. Memory_Module_lenovo* m2 = new Memory_Module_lenovo;
    80. cout << "第一台电脑" << endl;
    81. computer* com = new computer(c1,m1,g1);
    82. com->work();
    83. cout << "********************************" << endl;
    84. cout << "第二台电脑" << endl;
    85. computer* com1 = new computer(c2, m2, g2);
    86. com1->work();
    87. }
    88. int main() {
    89. fun();
    90. return 0;
    91. }

  • 相关阅读:
    ftp多用户多目录配置
    windows环境下mongodb 5.0.9分片集群环境搭建
    pgbouncer 使用
    猿创征文 第二季| #「笔耕不辍」--生命不息,写作不止#
    【第29例】IPD体系进阶:PL-TMT 产品线技术管理团队
    手把手怎么把照片修复高清,p图小白也能轻松上手
    leetcode42. 接雨水
    安装使用RocketMQ一套保姆全教程-最快完成SpringBoot使用消息队列demo
    判断是否工作在docker环境
    猿创征文|基于STM32设计的物联网熏艾空气消毒装置(STM32+华为云IOT)
  • 原文地址:https://blog.csdn.net/m0_73731708/article/details/132927521