文章目录
本站参考
前言
多态一词最初来源于希腊语,意思是具有多种形式或形态的情形,在C++中是指同样的消息被不同类型的对象接收时导致不同的行为,这里讲的消息就是指对象的成员函数的调用,而不同的行为是指不同的实现,也就是调用了不同的函数。简而言之就是“一种接口,多种实现(方法)”
静态绑定又称为前期绑定,在程序编译期间确定了程序的行为,也称静态多态。比如函数重载。在编译的时候确定了调用的函数。
动态绑定也称后期绑定,是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称动态多态。就像上面的,运行时在到虚表中找调用函数的地址。
多态,多数都是动态绑定。

静态多态
1.函数重载
函数重载是一种特殊情况,C++允许在同一作用域中声明几个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
在C++中不仅函数可以重载,运算符也可以重载。
2. 注意事项
静态多态的注意事项
1.函数名称必须相同。
2.参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
3.函数的返回类型可以相同也可以不相同。
4.仅仅返回类型不同不足以成为函数的重载。
5.函数的重载关系一定发在同一作用域下,不同作用下的同名函数构成的是隐藏关系。
6.C++函数的参数如果是指针类型的,也影响函数的重载,编译时就会在函数的末尾添加Px。
7.如果参数是指针或引用,是否加const也会影响函数的重载
3. 原理
3.1 C语言中为什么不能支持函数重载?

从上图可知编译器在编译.c文件时,只会给函数进行简单的重命名;具体的方法是给函数名之前加上”_”;所以加入两个函数名相同的函数在编译之后的函数名也照样相同;调用者会因为不知道到底调用那个而出错;
3.2 C++中函数重载底层是如何处理的?

在.cpp文件中,虽然两个函数的函数名一样,但是他们在符号表中生成的名称不一样。
‘?’表示名称开始,‘?’后边是函数名“@@YA”表示参数表开始,后边的3个字符分别表示返回值类型,两个参数类型。“@Z”表示名称结束。
由于在.cpp文件中,两个函数生成的符号表中的名称不一样,所以是可以编译通过的。
3.3 C++中能否将一个函数按照C的风格来编译
可以按照C风格来编译,只需在函数名前加 extern “C” 就可以完成按照C风格来编译
动态多态
1.条件
①被调用的函数必须是虚函数,并且派生类必须对基类的虚函数进行重写。
②必须通过基类的指针或者引用调用虚函数
第一个注意点是:调用的函数只能是虚函数,并且派生类必须对基类的虚函数进行重写。如果不重写,只是继承,实现方法一样。如果基类的不是虚函数,重写函数只是构成隐藏(跟没写一样)。
第二个注意点是:必须通过基类的指针或者引用调用虚函数。

2.虚函数 及其重写
2.1 虚函数
虚函数:被virtual修饰的成员函数称为虚函数。注意:修饰的是成员函数。
2.1.1 纯虚函数
在虚函数后面写上=0,这个函数称为纯虚函数。
virtual void funtion()=0;
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承之后也不能实例化出对象,只有重写的纯虚函数,派生类才能实例化出对象。
只有派生类经过纯虚函数重写才能实例化出对象。但是基类还是抽象类,不能实例化对象。
纯虚函数的作用:
1.一定程度上强制了派生类对纯虚函数的重写,如果不重写,派生类就不能实例化对象。
2.表示抽象的类型
2.2 虚函数的重写
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类的虚函数的返回值类型,函数名字,参数列表完全相同),但是函数的实现不同,称派生类重写了基类的虚函数。
在虚函数重写时,派生类的虚函数可以不加virtual关键字,也可以构成重写,因为继承,将基类的虚函数继承了下来,在派生类依旧保持虚函数的属性。但是这种写法不规范,不建议这样写。
注意
虚函数的重写,需要函数名,参数,返回值类型一样。但是派生类只是继承了函数的接口,接口就是函数名,参数,返回值类型。派生类重写只是实现不同。

2.2.1 协变
派生类重写基类虚函数,与基类虚函数的返回值不同。并且基类虚函数的返回值基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用,称为协变。
其返回值类型也可以是别的类的指针或者引用

2.2.2 析构函数重写
问题:

所以析构函数需要定义成虚函数,来构成多态:

这里是一个注意点,申请空间为派生类,但是赋值给基类时,需要将析构函数写成虚函数,构成多态。
2.2.3 函数重写关键字
1.virtual
函数重写必须是对虚函数进行重写,所以基类中的函数需要被virtual修饰
2.final
修饰虚函数,表示该虚函数不能被继承,不能进行重写
修饰类,类不能被继承
(在C++98中,为了不让类被继承,可以将基类的构造函数私有化(private),于是派生类就不能构造属于基类的成员。)


- override
检查派生类虚函数是否重写了基类的虚函数,如果没有编译错误。
这个只能修饰派生类的虚函数,不能修饰基类虚函数
override关键字最好用来检查派生类虚函数接口(函数名,参数,返回值)是否写错

3. 原理
3.1 虚函数表
3.1.1 基类虚函数表

通过观察我们发现,对象b是8个字节,除了_num外,还有_vfptr指针。这个指针是我们叫做虚函数表指针,简称虚表指针。一个含有虚函数的类中至少有一个这样的指针。
_vfptr指针变量保存的是虚函数表的起始地址。
虚函数表实际是一个函数指针数组,虚函数表简称虚表。虚表里面保存的都是虚函数的地址。
3.1.2 派生类虚函数表
1.派生类不重写基类的虚函数。

派生类会继承基类的虚函数,会继承基类的虚表。
但是派生类和基类的_vfptr变量内容不相等,说明两个虚表不是同一个虚表,只是虚表里的内容相同,所以会调用同一个函数。
2.派生类重写基类的虚函数。

派生类重写基类虚函数,会重写派生类虚函数表里的内容,将对应位置覆盖成重写虚函数的指针。
3.派生类添加基类之外的虚函数。

注意:
1.类中有虚函数只是这个类中多一个虚函数表指针,不是将虚函数表保存到类中。
2.虚函数表最后会以nullptr结尾。
3.同类型的对象共用一张虚表,可以理解成一个类的虚表属于这个类的,实例化的对象,都公用这一张虚表。
派生类的虚表:
1.派生类会继承基类的虚表,当两个虚表表不是一张虚表。派生类先将基表虚表的内容拷贝一份到派生类的虚表中
2.如果虚表重写虚函数,用派生类重写虚函数的地址覆盖掉虚表中对应虚函数的地址。
3.派生类增加虚函数,会在派生类虚表中声明次序增加到虚表的最后。
4.单继承。



5.多继承。



3.2 动态多态原理
多态是基于虚函数的虚函数表。构成多态,跟对象有关。如果是基类对象,会去基类的虚表中找要调用虚函数的地址,去执行虚函数的代码。如果是派生类对象,会去派生类类的虚表中找要调用虚函数的地址,去执行虚函数的代码。
派生类虚函数重写之后,可以实现不同的对象,有不同的实现方法,展现不同的效果。
本文详细介绍了C++中静态多态的函数重载和动态多态的虚函数概念、原理,包括虚函数的使用、纯虚函数、虚函数重写(协变、析构函数)、虚函数表的结构,以及动态绑定如何实现多态。
555

被折叠的 条评论
为什么被折叠?



