zoukankan      html  css  js  c++  java
  • 第49课 多态的概念和意义 (虚函数virtual)

    1. 函数重写回顾

     1 #include<iostream>     
     2 #include<string>
     3 
     4 //函数重写:子类重定义父类中已经存在的成员函数
     5 
     6 using namespace std;
     7 
     8 class Parent
     9 {
    10 public:
    11     void print()
    12     {
    13         cout << "I'm Parent" << endl;
    14     }
    15 };
    16 
    17 class Child : public Parent
    18 {
    19 public:
    20 
    21     void print()     //函数重写
    22     {
    23         cout << "I'm Child" << endl;
    24     }
    25 
    26 };
    27 
    28 //全局函数
    29 void how_to_print(Parent* p)
    30 {
    31     p->print();
    32 }
    33 
    34 int main()
    35 {
    36     Parent p;
    37     Child c;
    38 
    39     how_to_print(&p);        //期望I'm Parent       实际I'm Parent 
    40     how_to_print(&c);        //期望I'm Child        实际I'm Parent 
    41 
    42     //为什么出现以上??????编译器角度,在全局函数里,认为都调用父类的print()是最安全的
    43 
    44     return 0;
    45 }

    (1)父类被重写的函数依然会继承给子类-----只不过子类重写定义了同名函数

    (2)子类重写的函数覆盖父类中的函数

    (3)通过作用域分辨符(::)可以访问父类中的函数

    问:子类为什么要定义一个父类中出现的函数???

      父类定义的函数不能满足要求,所以要在子类重新定义--重写。

        所以期望重写后,只要是子类调用就是调用子类定义的函数版本,不然重写就没有意义。--------上面的代码就是没有满足预期的案列

    复制代码
    Child c;
    
    Parent* p = &c; 
    
    c.Parent::print();  //从父类中继承
    
    c.print();          //在子类中重写
     
    p->print();         //父类中定义---------不能满足预期,因为p指向的是子类对象,所以本应该调用子类函数
    复制代码

    2. 面向对象中期望的行为

    (1)根据实际的对象类型判断如何调用重写函数----而不是根据指针的类型

    (2)父类指针(引用)

      ①指向父类对象,则调用父类中定义的函数

      ②指向子类对象,则调用子类中定义的重写函数

    3. 面向对象中多态的概念--------同一行语句展现不同表现形态(多态本质

    (1)根据实际的对象类型决定函数调用的具体目标

    (2)同样的调用语句实际运行时有多种不同的表现形态

                              

    (3)C++语言直接支持多态的概念

      ①通过使用virtual关键字对多态进行支持

      ②被virtual声明的函数重写后具有多态特性

      ③被virtual声明的函数叫做虚函数

        ----------加virtual就可以改变编译器的行为--------------

    什么时候会使用虚函数-??------>定义类的时候,我们觉得某一个函数在后续被继承当中有可能被重写,就定义为virtual修饰该函数

     1 #include<iostream>
     2 #include<string>
     3 
     4 //多态:根据实际对象类型决定函数调用的具体目标  virtual  虚函数
     5 
     6 using namespace std;
     7 
     8 class Parent
     9 {
    10 public:
    11 
    12     virtual void print()                //可能之后继承中要被重写---加virtua---多态版本(和48课惟一的不同)
    13     {
    14         cout << "I'm Parent" << endl;
    15     }
    16 };
    17 
    18 class Child : public Parent
    19 {
    20 public:
    21     virtual void print()               //子类确实重写了父类print()---父类已加virtua,这里可以不用加(不加的话由于继承默认是虚函数)
    22     {
    23         cout << "I'm Child" << endl;
    24     }
    25 
    26 };
    27 
    28 //多态行为----加virtual就可以改变编译器的行为
    29 
    30 void how_to_print(Parent* p)
    31 {
    32     p->print();      //希望多态行为,根据指针p所指向的实际对象类型判断调用哪个重写函数
    33                      //子类调用子类函数,父类调用父类函数
    34 }                    //指针p指向父类对象------调用父类实现版本
    35                      //指针p指向子类对象------调用子类实现版本
    36 
    37 
    38 
    39 int main()
    40 {
    41     Parent p;
    42     Child c;
    43   
    44     how_to_print(&p);    //打印期望结果 I'm Parent  指针p所指向的实际对象是Parent类型
    45     how_to_print(&c);    //打印期望结果 I'm Child   指针c所指向的实际对象是Child类型
    46 
    47     //根据实际对象类型调用虚函数
    48 
    49     return 0;
    50 }

    4. 多态的意义

    (1)在程序运行过程中展现出动态的特性--------就是在编译期间无法确定究竟调用了哪个版本的函数实现

    (2)函数重写必须多态实现否则没有意义---------但凡产生了函数重写,必须用多态的实现,实际工程中,子类重写了父类里的成员函数,那么必须父类对应的成员函数就要定义为virtual虚函数,也就是希望父类来继承,来实现多态特性 ---------被重写的函数一定是虚函数

    (3)多态面向对象组件化程序设计的基础特性

    5. 理论中的概念 

    (1)静态联编:在程序的编译期间能确定具体的函数调用---------多态指的就是理论的静态联编

           -------------如:函数重载:编译期间通过函数参数的类型和个数确定具体调用哪一个

    (2)动态联编:在程序实际运行后才能确定具体的函数调用

                   -------------如:函数重写:前面例子p指针去调用print(),同一个函数调用表现的行为和结果不一样,编译期间无法确定)

    (3)小结

      ①使用父类指针(引用)函数前virtual为动态联编没加virtual为静态联编

      ②使用父类或子类自身声明对象加不加virtual都是静态联编的

    程序说明:

     1 #include<iostream>
     2 #include<string>
     3 
     4 //动态联编与静态联编
     5 
     6 using namespace std;
     7 
     8 class Parent 
     9 {
    10 public:
    11     virtual void func()      //出现virtual,也就是类Parent希望被继承  
    12     {
    13         cout << "void func()" << endl;
    14     }
    15 
    16     virtual void func(int i)
    17     {
    18         cout << "void func(int i):" << i << endl;
    19     }
    20 
    21     virtual void func(int i, int j)   //子类进行了重写,而且还有virtual修饰,表示希望展示多态行为
    22     {
    23         cout << "void func(int i, int j):" << "(" << i << "," << j << ")" << endl;
    24     }
    25 };
    26 
    27 class Child : public Parent
    28 {
    29 public:
    30 
    31     void func(int i, int j)   //也是虚函数,重写了父类成员函数
    32     {
    33         cout << "void func(int i, int j) : " << i + j << endl;
    34     }
    35 
    36     void func(int i,int j,int k)  //子类定义和父类的同名函数,会发生同名覆盖(作用域不同),将父类三个func()覆盖  
    37     {                              //Child子类里面的func()不会发生同名覆盖,同名覆盖只能发生在父类子类之间
    38     
    39         cout << "void func(int i, int j,int k) : " <<i+j+k<< endl;
    40     }
    41 };
    42 
    43 //全局函数
    44 void run(Parent* p)
    45 {
    46     p->func(1, 2);             //展现多态特征--- 根据指针p所指向的实际对象类型判断调用哪个重写函数 
    47                                //动态联编-------同一行语句展现不同的调用结果
    48 }
    49 
    50 int main()
    51 {
    52     Parent p;
    53 
    54     //编译器在编译期间就知道调用哪个函数-----重载函数调用----静态联编
    55 
    56     p.func();        //调用父类  void func()
    57     p.func(1);        //调用父类  void func(int i):1
    58     p.func(1, 2);    //调用父类  void func(int i, int j):(1,2)
    59 
    60     cout << endl;
    61 
    62     Child c;
    63     c.func(1, 2);   //调用子类重写函数  void func(int i, int j ): 3   静态联编
    64 
    65     cout << endl;
    66 
    67     //父类指针p调用重写函数func(1,2)
    68     run(&p);        //调用父类   动态联编  void func(int i, int j):(1,2)
    69     run(&c);        //调用子类   动态联编  void func(int i, int j ): 3 
    70 
    71     return 0;
    72 }

    【编程实验】江湖恩怨

     1 #include <iostream>
     2 #include <string>
     3 
     4 //场景:庄主打老怪
     5 
     6 using namespace std;
     7 
     8 //老怪
     9 class Boss
    10 {
    11 public:
    12     //无名怪招
    13     int fight()
    14     {
    15         int ret = 10; //威力10
    16         cout << "Boss::fight(): " << ret << endl;
    17         return ret;
    18     }
    19 };
    20 
    21 //老庄主
    22 class Master
    23 {
    24 public:
    25     //八剑齐飞
    26     virtual int eightSwordKill()
    27     {
    28         int ret = 8; //威力10
    29         cout << "Master::eightSwordKill(): " << ret << endl;
    30         return ret;
    31     }
    32 };
    33 
    34 
    35 //少庄主
    36 class NewMaster : public Master
    37 {
    38 public:
    39     //八剑齐飞
    40     int eightSwordKill()                //函数重写-----重写父类Master的成员函数eightSwordKill()--实现更多的功能
    41     {
    42         //少庄主对老庄主的八剑齐飞进行改造,练就了双臂的八剑齐飞
    43         //威力大增
    44         int ret = Master::eightSwordKill() * 2; //威力是老庄主的2倍
    45         cout << "NewMaster::eightSwordKill(): " << ret << endl;
    46         return ret;
    47     }
    48 };
    49 
    50 
    51 //战场pk
    52 void field_pk(Master* master, Boss* boss)   //赋值兼容性---NewMaster也可以成为Master*
    53 {
    54     int k = master->eightSwordKill();
    55     int b = boss->fight();
    56 
    57     if (k < b)
    58     {
    59         cout << "Master is killed..." << endl;
    60     }
    61     else
    62     {
    63         cout << "Boss is killed..." << endl;
    64     }
    65 
    66 }
    67 
    68 int main()
    69 {
    70     Master master;
    71     Boss boss;
    72 
    73     cout << "Master vs Boss" << endl;
    74 
    75     field_pk(&master, &boss);
    76 
    77     cout << endl;
    78 
    79     cout << "NewMaster vs Boss" << endl;
    80 
    81     NewMaster newMaster;
    82 
    83     field_pk(&newMaster, &boss);
    84 
    85     return 0;
    86 
    87 }

    /*输出结果

    Master vs Boss

    Master::eightSwordKill(): 8

    Boss::fight(): 10

    Master is killed...

    NewMaster vs Boss

    Master::eightSwordKill(): 8

    NewMaster::eightSwordKill(): 16

    Boss::fight(): 10

    Boss is killed...

    */

    6. 小结

    (1)函数重写只可能发生在父类与子类之间

    (2)根据实际对象的类型确定调用的具体函数

    (3)virtual关键字是C++支持多态的唯一方式

    (4)被重写的虚函数可表现出多态的特性

    (5)多态本质同样的语句有不同的调用结果-------概念里面的动态联边,要等要运行时刻,才能知道调用的是父类还是子类实现

    (6)多态可以很好地描述显示生活中的实例

  • 相关阅读:
    array with objects sort
    Vue组件之区域滚动
    ajax跨域请求问题总结
    常见contentType
    Sublime Text 3实用快捷键大全
    具有层级关系的扁平化数组转化成树形结构数组
    阿里云地图选择器
    前端解析二进制文件流并下载
    tool.js日常工具方法
    git操作
  • 原文地址:https://www.cnblogs.com/liuyueyue/p/13388318.html
Copyright © 2011-2022 走看看