今天来学习一个新的概念,多态!!!多态在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语言,数据结构等技术问题。