当前位置: 首页 > news >正文

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

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

  • 一、多态的概念及实现
    • 1.1、什么是多态
    • 1.2、实现多态的条件
    • 1.3、实现继承与接口继承
    • 1.4、多态中的析构函数
    • 1.5、抽象类
  • 二、多态的实现原理

一、多态的概念及实现

1.1、什么是多态

多态的概念:

在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。
计算机程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作。
多态也可定义为“一种将不同的特殊行为和单个泛化记号相关联的能力”。
多态可分为变量多态与函数多态。变量多态是指:基类型的变量(对于C++是引用或指针)可以被赋值基类型对象,也可以被赋值派生类型的对象。函数多态是指,相同的函数调用界面(函数名与实参表),传送给一个对象变量,可以有不同的行为,这视该对象变量所指向的对象类型而定。因此,变量多态是函数多态的基础。

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态 。
举个例子:比如 买票这个行为 ,当 普通人 买票时,是全价买票; 学生 买票时,是半价买票; 军人
买票时是优先买票。

1.2、实现多态的条件

实现多态的两个条件:

1、被调用的函数必须是虚函数,子类对父类的虚函数进行重写 (重写:三同(函数名/参数/返回值)+虚函数)
2、父类指针或者引用去调用虚函数。

举个例子:
在这里插入图片描述
这时候就实现了多态,即指向子类对象就调用子类对象的函数,如果指向的是父类对象,就调用的是父类对象的函数:
在这里插入图片描述
其实C++这里还有一个特殊情况,就是如果父类的同名函数加上了virtual修饰了,那么子类的同名函数就算不加virtual也是虚函数了,即也构成多态:
在这里插入图片描述
但我个人感觉函数加上的好,因为可能会形成误导。

强调:一定要是父类的指针或引用调用,如果是对象就变成了普通调用了:
在这里插入图片描述
此外虚函数的重写也还要满足三同:函数名、参数、返回值相同,只要有其中一个不满足也会变成普通调用。

但是这里还有非常尴尬的例外:“协变”,含义是虚函数的返回值类型可以不同,但又一个条件:子类和父类的返回值类型也必须是父子关系指针和引用。
在这里插入图片描述
其实“协变”也是C++常常被诟病的一点,因为它的应用场景实在太局限了,我也是感觉它有点儿多余了,我们只需要了解一下即可。

1.3、实现继承与接口继承

普通函数的继承实际上是一种实现继承,也就是继承了函数的逻辑:
例如:
在这里插入图片描述
这里继承的是函数的实现,所以变量_a改变了,输出的结果也就改变了。
而虚函数继承的是函数的接口,也就是父类和子类的接口是一样的,只是实现的逻辑不一样。其目的主要是为了重写,达成多态。
例如:
在这里插入图片描述
因为这里继承的只是接口,而实现逻辑是不同的,所以打印出来的内容也就不同。也就实现了多态。
之所以说是子类继承了父类的接口,是因为如果我们改变子类中的虚函数的默认参数是不起作用的:
在这里插入图片描述
所以这也就解释了为什么子类的虚函数没有加virtual也依然是虚函数,因为其接口就是继承了父类的。

1.4、多态中的析构函数

我们先来看一个现象:
在这里插入图片描述
相信大家都能看出这段代码的问题,这很明显值是一个普通调用。但是它new了一个B对象却只调用了A类的析构函数,这岂不是有内存泄漏的风险?
那该怎么解决这个问题呢?如果要将析构函数也实现成多态的调用的话,那子类和父类的析构函数名不可能相同啊,不是冲突了?

C++正是为了解决这个问题,对构造函数进行了一些处理:
因为多态的原因,编译器在底层会将析构函数的函数名统一处理成destructor()。
所以我们表面上看到的析构函数是是不同名的,实际在底层他们都叫destructor(),所以也就能实现多态了:
在这里插入图片描述

1.5、抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。
抽象类还有以下三个注意点:

1、子类继承抽象类后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。
2、父类的纯虚函数强制了派生类必须重写,才能实例化出对象。
3、纯虚函数也可以写实现{ },但没有意义,因为是接口继承,而子类被强制了重写纯虚函数,所以{ }中的实现会被重写;父类没有对象,不能调用父类的实现,所以父类实现纯虚函数也就没有意义了。

其实各种抽象的事物都可以定义成抽象类,比如人、动物、汽车、水果……,也就是它不具体指哪一个事物,只是抽象的代表默写事物的总体特征。

比如说动物:
在这里插入图片描述

二、多态的实现原理

这里有一个类,我们试试来求一下它的大小:
在这里插入图片描述
首先要说一点,不管是普通成员函数还是虚函数都是不储存在类里面的,都是存在代码段的。
可这里的类的大小为什么是8字节呢?不应该是4字节吗?
说明类里面一定还存了别的什么东西,我们可以到监视窗口看看:
在这里插入图片描述
我们会发现除了成员_a之外还多了一个_vfptr的东西,这个其实是一个虚表指针,它的本质是一个数组指针,指向一个函数指针数组,而被指向的这个函数指针数组就是虚表。
由于平台的不同,虚表的位置也有可能不同,有的实在类的最前面有的可能是在类的最后面。
一个含有虚函数的类至少有一个虚表指针。
我们可以到内存中去仔细的看看A类的结构:
在这里插入图片描述
然后我们可以来看看虚表中到底有什么:
在这里插入图片描述
所以我们可以来打印一下虚表中的内容,看看它们是不是函数的地址,如果是的话试试调用它:
在这里插入图片描述
从结果来看确实是函数的地址,因为所有的虚函数的地址都会存进虚表,所以这里会打印四个。

有了上面的铺垫我们就可以来解释多态的真正原理了。
我们先让一个B类继承A类,并重写func函数:
在这里插入图片描述
然后我们再取出A类和B类的虚表对比看看:
在这里插入图片描述
我们发现两个对象中的虚表里,只有被重写的func()函数的地址不同,而没有重写的print1()的地址则相同。所以虚函数的重写也被称为是虚函数的覆盖(其实是虚函数表的覆盖)。

有了以上的铺垫,在我提出以下结论的时候,才会逻辑自掐:
多态的实现机制其实就是,傻傻地通过虚表指针找到虚表,再找到对应的虚函数。

之所以这种“傻傻”的行为能成功,是因为在父类指针或引用指向子类的时候会发生“切片”:
在这里插入图片描述
A类的指针只会指向B对象中A类部分的内容,所以也就只会在A类部分的虚表中查找。就算B类有多张虚表(当B类有多个直接父类时候就会有多张虚表)。父类A的指针通过切片之后也只会指向A类部分。
且因为,虚表的位置在某个类中都是固定的,所以偏移量也都是固定的,所以B类有多少个直接父类,他们父类的指针的寻址操作也都是统一的。

相关文章:

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

【C笔记】C多态 一、多态的概念及实现1.1、什么是多态1.2、实现多态的条件1.3、实现继承与接口继承1.4、多态中的析构函数1.5、抽象类 二、多态的实现原理 一、多态的概念及实现 1.1、什么是多态 多态的概念: 在编程语言和类型论中,多态(英…...

不想改代码!这样实现Reverse Sync测量时间同步精度

TSN的时间同步精度,指被测时钟与主时钟的最大偏差。在设备的组网过程中,最大的困难就是保证期望的时间同步精度。主时钟仅负责将自身的时间分发出去,难以判断其他设备的同步效果;此外,若在网络中某处发生了同步故障&am…...

【webrtc】 对视频质量的码率控制的测试与探索

目录 环境设置 transport-cc goog-remb (webrtc中的两种码率算法) 修改成remb算法 测试 效果 后续 可参考工程 环境设置 要到meshx上操作 telnet 112 然后执行factory_env show |grep meshx_ip 之后telnet meshx_ip 用户名admin 密码****.119 执行一下r…...

2003 - Can‘t connect to MysQL server on ‘39.108.169.0‘ (10060 “Unknown error“)

问题描述 某天和往常一样启动java项目,发现数据库出问题了,然后打开navicat,发现数据库的链接都连接不上, 一点击就会弹出报错框: 然后就各种上网搜索。 解决方案 上网查了一些解决方案,大部分都是说看…...

Python算法——选择排序

选择排序(Selection Sort)是一种简单的排序算法,它的基本思想是在未排序的部分中选择最小(或最大)的元素,然后将其放在已排序部分的末尾。选择排序不同于冒泡排序,它不需要反复交换元素&#xf…...

从「码农」到管理者,E人程序员的十年蜕变

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 当我们谈论程序员创业时,常常会首先想到一些传统观念认为的挑战:沟通技巧不佳、逻…...

ant Java任务的jvmargs属性和<jvmarg>内嵌元素

ant的Java任务可以在运行Apache Ant的Java虚拟机内、或者启用另外的Java虚拟机运行一个Java类。 可以使用java任务的jvmargs属性,设置传递给在新进程中的java虚拟机的参数。但当java任务的fork禁用的时候,jvmargs属性会被忽略。jvmargs这个属性已经被废…...

XML External Entity-XXE-XML实体注入

XML 实体? XML 实体允许定义标签,在解析 XML 文档时这些标签将被内容替换。一般来说,实体分为三种类型: 内部实体 外部实体 参数实体。 必须在文档类型定义(DTD)中创建实体 一旦 XML 文档被解析器处理,它将js用定义的常量“Jo Smith”替换定义的实体。正如您所看到…...

生态扩展Spark Doris Connector

生态扩展Spark Doris Connector doris官网去查找相匹配的spark spark的安装: tar -zxvf spark-3.1.2-bin-hadoop3.2.tgzmv spark-3.1.2-bin-hadoop3.2 /opt/sparkspark环境配置:vim /etc/profile export SPARK_HOME/opt/spark export PATH$PATH:$SPAR…...

构建 hive 时间维表

众所周知 hive 的时间处理异常繁琐且在一些涉及日期的统计场景中会写较长的 sql,例如:周累计、周环比等;本文将使用维表的形式降低时间处理的复杂度,提前计算好标准时间字符串未来可能需要转换的形式。 一、表设计 结合业务场景常…...

Pycharm安装jupyter和d2l

安装 jupyter: jupyter是d2l的依赖库,没有它就用不了d2l pycharm中端输入pip install jupyter安装若失败则: 若网速过慢,则更改镜像源再下载: pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ pip …...

虹科案例 | AR内窥镜手术应用为手术节约45分钟?

相信医疗从业者都知道,在手术室中有非常多的医疗器械屏幕,特别是内窥镜手术室中医生依赖这些内窥镜画面来帮助病患进行手术。但手术室空间有限,屏幕缩放位置相对固定,在特殊场景下医生观看内窥镜画面时无法关注到病患的状态。这存…...

纳米银线 纳米银纳米线 平均直径: 50-100nm

(西)纳米银线 (安)含量(%):99.9 (瑞)平均直径: 50-100nm (20nm 30nm 60nm ) (禧)长度:10um …...

力扣labuladong——一刷day15

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣92. 反转链表 II二、力扣206. 反转链表 前言 一、力扣92. 反转链表 II /*** Definition for singly-linked list.* public class ListNode {* int…...

【开题报告】基于微信小程序的母婴商品仓储管理系统的设计与实现

1.研究背景 母婴商品是指专门为婴幼儿和孕产妇提供的各类产品,如婴儿奶粉、尿布、奶瓶、洗护用品等。随着社会经济的发展和人们对婴幼儿健康关注度的提高,母婴商品市场呈现出快速增长的趋势。同时,电子商务的兴起和互联网技术的发展&#xf…...

Faraday库

require faraday# 创建Faraday对象,使用作为代理服务器 proxy_host huake proxy_port 1111 faraday Faraday.new(:proxy > { :host > proxy_host, :port > proxy_port })# 使用Faraday对象发送GET请求到https://www.dianping.com/ response faraday.get…...

【原创】java+swing+mysql校园论坛管理系统设计与实现

摘要: 随着互联网技术的不断发展,论坛作为一种信息交流和互动的平台,在学校中发挥着越来越重要的作用。校园论坛管理系统是为了方便学校管理论坛、提高论坛的互动性和用户体验而设计的一款系统。一般的论坛网站都是B/S架构,也就是…...

endnote调整参考文献

endnote调整参考文献 1. 2. 3.自定义GBT7714!!!...

chap认证带客户端IP分配案例

PPP协议两边的网段可以不在同一个网段,因为数据链路帧用0xff表示帧,不用arp,所以可以不同网段。 R1: aaa local-user test password cipher admin local-user test service-type ppp interface Serial4/0/0 link-protocol ppp pp…...

算法笔记【8】-合并排序算法

文章目录 一、前言二、合并排序算法基本原理三、实现步骤四、优缺点分析 一、前言 合并排序算法通过采用分治策略和递归思想,实现了高效、稳定的排序功能。本文将深入探讨合并排序算法的原理、实现步骤,并讨论其优缺点。 二、合并排序算法基本原理 合…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

无法与IP建立连接,未能下载VSCode服务器

如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色&#xf…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes&#xff0…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

AspectJ 在 Android 中的完整使用指南

一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...