【C++笔记1】C++ 多态

本站参考

1. 静态多态
函数重载
2. 动态多态:概念、原理、示例(虚函数、需基表)
动态多态
虚函数

前言

多态一词最初来源于希腊语,意思是具有多种形式或形态的情形,在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),于是派生类就不能构造属于基类的成员。)

在这里插入图片描述
在这里插入图片描述

  1. 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 动态多态原理

多态是基于虚函数的虚函数表。构成多态,跟对象有关。如果是基类对象,会去基类的虚表中找要调用虚函数的地址,去执行虚函数的代码。如果是派生类对象,会去派生类类的虚表中找要调用虚函数的地址,去执行虚函数的代码。
派生类虚函数重写之后,可以实现不同的对象,有不同的实现方法,展现不同的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值