今天来学习一个新的概念,多态!!!多态在C++编程中具有重要的地位与意义,是面向对象的一个重要思想! 
 加qq1126137994一起学习更多技术~
1、问题引入
父类与子类之间具有赋值兼容性;
*子类对象可以当做父类对象使用(赋值兼容性)
- 子类对象可以直接赋值给父类对象
 - 子类对象可以直接初始化父类对象
 - 父类指针可以直接指向子类对象
 - 父类引用可以直接引用子类对象
 
看一个例子程序来理解一下:
#include <iostream>
#include <string>using namespace std;class Parent
{
public:int mi;void add(int i){mi += i;}void add(int a, int b){mi += (a + b);}
};class Child : public Parent
{
public:int mv;void add(int x, int y, int z){mv += (x + y + z);}
};int main()
{Parent p;Child c;p = c;Parent p1(c);Parent& rp = c;Parent* pp = &c;rp.mi = 100;rp.add(5);             // 没有发生同名覆盖?rp.add(10, 10);        // 没有发生同名覆盖?/* 为什么编译不过? */// pp->mv = 1000;      // pp是父类指针,指向子类c,那么c对象就退化为父类对象// pp->add(1, 10, 100); // pp只能访问父类对象,并且可以访问父类与子类同名的对象// 而不用指定作用域。return 0;
} 
-当使用父类指针(引用)指向子类对象时
- 子类对象退化为父类对象
 - 只能访问父类中定义的成员
 - 可以直接访问被子类覆盖的同名成员而不用指定作用域
 
2、函数重写
- 子类中可以重定义父类中已经存在的成员函数
 - 这种重定义发生在继承中,叫做函数重写
 - 函数重写是函数同名覆盖的特殊情况
 
那么当函数重写遇上赋值兼容会发生什么? 
 先来看一个简单的例子:
#include <iostream>
#include <string>using namespace std;class Parent
{
public:int mi;void add(int i){mi += i;}void add(int a, int b){mi += (a + b);}void print(){cout << "I'm Parent." << endl;}
};class Child : public Parent
{
public:int mv;void add(int x, int y, int z){mv += (x + y + z);}void print(){cout << "I'm Child." << endl;}
};void how_to_print(Parent* p)
{p->print();
}int main()
{Parent p;Child c;how_to_print(&p);    // Expected to print: I'm Parent.how_to_print(&c);    // Expected to print: I'm Child.return 0;
} 
运行结果为: 
 I’m Parent. 
 I’m Parent.
很显然,这个结果并不是我们想要的,我们想要的是执行 how_to_print(&c); 后打印I’m Child. 
 这就是函数print重写后,遇到赋值兼容的情况。
问题分析:
- 编译期,间编译器只能根据指针的类型判断所指向的对象
 - 根据赋值兼容,编译器认为父类指针指向的是父类对象
 - 因此,编译结果只可能是调用父类中定义的同名的函数
 
小结:
- 子类对象可以当做父类对象使用(赋值兼容)
 - 父类指针可以正确的指向子类对象
 - 父类引用可以正确的代表子类对象
 - 子类中可以重写父类的成员函数
 
3、解决办法-多态的概念
- 父类中被重写的函数依然会继承给子类
 - 子类中被重写的函数会覆盖父类中的同名函数 
 
而面向对象期望的行为是:
- 根据实际的对象类型调用具体的成员重写函数
 - 父类指针(引用)指向 
*父类对象,则调用父类中定义的函数
*子类对象,则调用子类中定义的重写函数 
要实现这个行为,就需要引出面向对象中的多态的概念: 
 面向对象中多态的概念: 
 - 根据实际的对象类型,决定函数调用的具体目标 
 - 通用的调用语句,在实际的运行中有多种不同的表现形态 
 
C++语言直接支持多态的概念
- 通过使用virtual关键字对多态进行支持
 - 被virtual声明的函数被重写后具有多态性
 - 被virtual声明的函数叫做虚函数
 
修改上一个程序:
#include <iostream>
#include <string>using namespace std;class Parent
{
public:int mi;virtual void print(){cout << "I'm Parent." << endl;}};class Child : public Parent
{
public:int mv;void print(){cout << "I'm Child." << endl;}
};void how_to_print(Parent* p)
{p->print();
}int main()
{   Parent p;Child c;how_to_print(&p);how_to_print(&c);return 0;
}
 
运行结果: 
 I’m Parent. 
 I’m Child.
可以看出,这是我们想要的结果。
多态的意义:
- 在程序运行过程中展现出动态特性
 - 函数重写必须多态实现,否则没有意义
 - 多态是面向对象组件化程序设计的基础特性
 
4、多态在理论中的概念与意义
理论中的概念:
- 静态联编 
*在函数编译期间就能确定具体的函数调用
-如.函数重载 - 动态联编 
*在程序实际运行后才能确定函数的具体调用
-如.函数重写 
给个例子说明:
#include <iostream>
#include <string>using namespace std;class Parent
{
public:virtual void func(){cout << "void func()" << endl;}virtual void func(int i){cout << "void func(int i) : " << i << endl;}virtual void func(int i, int j){cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;}
};class Child : public Parent
{
public:void func(int i, int j){cout << "void func(int i, int j) : " << i + j << endl;}void func(int i, int j, int k){cout << "void func(int i, int j, int k) : " << i + j + k << endl;}
};void run(Parent* p)
{p->func(1, 2);     // 展现多态的特性// 动态联编
}int main()
{Parent p;p.func();         // 静态联编p.func(1);        // 静态联编p.func(1, 2);     // 静态联编cout << endl;Child c;c.func(1, 2);     // 静态联编cout << endl;run(&p);run(&c);return 0;
}
 
上述程序的运行结果为: 
 void func() 
 void func(int i) : 1 
 void func(int i, int j) : (1, 2)
void func(int i, int j) : 3
void func(int i, int j) : (1, 2) 
 void func(int i, int j) : 3
5、拓展训练
下面是一个展现多态,继承的程序,参考教学视频第49课第二个视频!
#include <iostream>
#include <string>using namespace std;class Boss
{
public:int fight(){int ret = 10;cout << "Boss::fight() : " << ret << endl;return ret;}
};class Master
{
public:virtual int eightSwordKill(){int ret = 8;cout << "Master::eightSwordKill() : " << ret << endl;return ret;}
};class NewMaster : public Master
{
public:int eightSwordKill(){int ret = Master::eightSwordKill() * 2;cout << "NewMaster::eightSwordKill() : " << ret << endl;return ret;}
};void field_pk(Master* master, Boss* boss)
{int k = master->eightSwordKill();int b = boss->fight();if( k < b ){cout << "Master is killed..." << endl;}else{cout << "Boss is killed..." << endl;}
}int main()
{Master master;Boss boss;cout << "Master vs Boss" << endl;field_pk(&master, &boss);cout << "NewMaster vs Boss" << endl;NewMaster newMaster;field_pk(&newMaster, &boss);return 0;
}
 
运行结果为: 
 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、总结
- 函数重写只可能发生在父类与子类之间
 - 根据实际对象的类型调用具体的函数,多态性
 - virtual关键字是C++中支持多态的唯一方法
 - 被重写的虚函数可以表现出多态性
 
想获得各种学习资源以及交流学习的加我: 
 qq:1126137994 
 微信:liu1126137994 
 可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。