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