多态多态在维基百科)中的定义如下:
在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。
多态按照字面意思来理解,就是多种形态,当我们定义的类之间存在层次的结构时,并且类之间是通过继承来关联的时候,就会用到多态,多态意味着调用成员函数的时候,会根据函数的调用的对象的类型来执行不同的函数。
试想下面的情景,在一个全球性聊天软件中,对于不同国籍的人来说,系统检测到当前用户的国籍即输出当前用户所说的语言。我们设计出如下代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define r register
#define ll longlong
#define pi 3.141
#define inf 1400000000
usingnamespace std;
classperson{
public:
person(){
}
void speak(){
printf(speakn);
}
};
classchinese:publicperson{
public:
chinese(){
}
void speak(){
printf(speak chinesen);
}
};
classenglish:publicperson{
public:
english(){
}
void speak(){
printf(speak englishn);
}
};
int main(){
chineseperson_chinese;
englishperson_english;
person_chinese.speak();
person_english.speak();
return0;
}
运行上面的代码,输出的结果为:
speak chinese
speak english
我们换一个思路,如果这三个人都是人那么我需要在不同国籍的人后面输出他们所说的语言:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define r register
#define ll longlong
#define pi 3.141
#define inf 1400000000
usingnamespace std;
classperson{
public:
inttype;
public:
person(){
type=0;
}
void speak(){
printf(speakn);
}
};
classchinese:publicperson{
public:
chinese(){
type=1;
}
void speak(){
printf(speak chinesen);
}
};
classenglish:publicperson{
public:
english(){
type=2;
}
void speak(){
printf(speak englishn);
}
};
int main(){
chinesechinese_person_1;
chinesechinese_person_2;
englishenglish_person_1;
person* people[3];
people[0]=&chinese_person_1;
people[1]=&chinese_person_2;
people[2]=&english_person_1;
for(r int i =0; i type==1){
chinese* person =(chinese*)people[i];
person->speak();
}
elseif(people[i]->type==2){
english* person =(english*)people[i];
person->speak();
}
}
return0;
}
尝试编译这段代码输出了同样的结果,但是如果这样写代码,写出来的代码将会非常冗余,我们可以尝试在父类的函数前加上virtual关键字,从而实现相同的功能。
考虑下为什么不加关键字,程序的输出结果为:
person1:speak
person2:speak
person3:speak
导致程序错误输出的原因是,调用函数speak被编译器设置为基类中的版本,这就是所谓的静态多态,/静态链接,也就是说函数调用在程序执行前就已经准备好了,这种情况被称之为早绑定,因为函数在程序编译的时候就已经被设置好了,当我们使用virtual关键字的时候,编译器看的是指针的内容,而不是指针的类型,因此将子类的地址提取出来会调用各自的speak函数。
正如上所示,每一个子类都有一个函数speak独立实现,这就是多态的一般的使用方式,有了多态就会有多个不同的类,并且可以实现同一个名称但是不同作用的函数,甚至函数的参数可以完全相同。改进后的程序如下所示:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define r register
#define ll longlong
#define pi 3.141
#define inf 1400000000
usingnamespace std;
classperson{
public:
inttype;
public:
person(){
type=0;
}
void speak(){
printf(speakn);
}
};
classchinese:publicperson{
public:
chinese(){
type=1;
}
void speak(){
printf(speak chinesen);
}
};
classenglish:publicperson{
public:
english(){
type=2;
}
void speak(){
printf(speak englishn);
}
};
int main(){
chinesechinese_person_1;
chinesechinese_person_2;
englishenglish_person_1;
person* people[3];
people[0]=&chinese_person_1;
people[1]=&chinese_person_2;
people[2]=&english_person_1;
for(r int i =0; i speak();
}
return0;
}
虚函数虚函数是指在父类中定义的使用关键字virtual声明的函数,在子类中需要重新定义父类中定义的虚函数的时候程序会告诉编译器不要静态链接到这个函数。
有些时候,我们编写程序,需要的是在程序中的任意点可以根据所调用的对象的类型来选择我们需要调用的函数,这种操作被称为动态链接或者后期绑定。
纯虚函数
如果我们在进行程序设计的时候,可能在父类中无法或者不需要对虚函数给出一个有意义的实现,那么此时就需要用到纯虚函数,我们可以将父类中的虚函数改为如下形式:
classperson{
public:
inttype;
public:
person(){
type=0;
}
virtualvoid speak()=0;
};
通过上面的方法,我们告诉编译器这个函数没有主体,就是一个纯虚函数。
动态多态的条件1.父类中必须包含虚函数,并且子类中一定要对父类中的虚函数进行重写。
2.通过基类对象的指针或者引用进行调用虚函数
重写1.基类中将要被重写的函数必须是虚函数
2.基类的派生类中的虚函数的原型必须保持一致,协变函数和析构函数(父类和子类的析构哈桑农户不同)除外
3.访问限定符不同
协变函数 :父类(子类)的虚函数返回父类(子类)的指针或者引用
不能被定义为虚函数的子类1.友元函数,不是类的成员函数
2.全局函数
3.静态成员函数,没有this指针
4.构造函数,拷贝构造函数,赋值运算符重载(赋值运算符可以作为虚函数但是不建议作为虚函数)
总结一些要点包含纯虚函数的类被称为抽象类(也成为接口类),抽象类不能实例化出对象,纯虚函数在子类中重新定义之后,子类才能实例化出对象,纯虚函数一定要被继承,否则纯虚函数的存在没有任何意义,纯虚函数一定没有定义,纯虚函数的存在就是用来规范子类的行为。
对于虚函数来说,父类和子类都有各自的版本,由多态方式调用的时候动态绑定。
实现了纯虚函数的子类,这个纯虚函数在子类中就变成了虚函数,子类的子类可以覆盖整个虚函数,由多态的方式调用的时候进行动态绑定。
在有动态分配的堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚函数。
友元函数不等于成员函数,只有成员函数才可以是虚函数,因此友元函数不能是虚函数,但是可以通过让友元函数调用虚拟成员函数来解决友元的虚拟问题。
析构函数应该是虚函数,将调用相应的对象类型的析构函数,因此如果指针指向的是子类的对象,将调用子类的洗后函数,然后再自动调用父类的析构函数。
区块链的电子认证应用在教育方面有哪些好处
今年最后一次,SpaceX宣布两周发布60颗Starlink卫星
iPhone 8 什么时候上市?最新消息 如果iPhone 8 真的有刘海,那APP 可能这样显示
双十二苹果无线充电宝分享:南卡无线充电宝POW2体验
诺基亚6只是开胃菜?下月满血骁龙835版本诺基亚8亮相!
C++的多态详解
元宇宙勾连的现实与虚拟
ai芯片和存储芯片的区别
怎样学习嵌入式比较好
互联网的发展导致营销渠道、营销成本发生变化
解决方案 | 入门级直流PLC模块
iphone xr和xs的区别 哪个更值得入手
实时设备管理系统怎样去建设
华为加快自有芯片研发,有望成台积电7nm最大客户
智能电动车企业零跑科技完成B轮融资
种子脱粒机的操作方法以及使用效果的介绍
人脸识别技术的边界在哪里?
超声波物位计的特点
麒麟5g芯片有哪些 麒麟9000是5g芯片吗
小米6现货发售了!不仅现货发售还有机会立减500,你期待吗?