第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)多态可以很好地描述显示生活中的实例

posted @ 2020-09-02 21:24  六月iii  阅读(324)  评论(0编辑  收藏  举报