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

C++类与对象-继承和多态(超全整理)

前言

        前面讲类与对象上中下时,所讲的都是在单个类中相关的语法(初始化列表、this指针、静态成员、常函数和常对象......)或者使两个不同的类产生联系的语法(友元)。而本文虽然也是类与对象的内容,但和之前的有所区别。继承和多态这两个技术解决的是由单个类衍生出多个与此相似或相关的类。例如继承,子类继承父类的相关属性进而实例化出属于子类的对象,这极大地减少了相似的代码量、提高了执行效率。下面会分别向大家介绍继承和多态的语法,最后会附上一个项目来更好地让大家上手应用。


继承

       在现实中继承一词代表传承,例如我们常说的继承家产、继承家业这类的都是传承旧业的体现。在C++中也同样,利用继承这一语法技术能够将基类的某些属性传承到派生类中,从而避免代码出现冗余。其中被继承的类被称为基类或者父类,继承后的类被称为派生类或者子类。

继承语法

        class 子类名 : 继承方式 父类名 {};

一些概念:

单继承:单个父类能够被多个子类继承,即C++允许一父多子。

多继承:单个子类能够继承多个父类,即C++允许一个子类认多个父类。

菱形继承(钻石继承):多个子类继承同一个父类后,同一个孙类继承了这些子类,此时这种关系被称为菱形继承关系。该过程如下图所示:

1b3610c45c714364b88485e92d4acfed.jpg

 继承方式

          继承方式有三种分别为,public(公共继承)protected(保护继承)private(私有继承)

父类和子类的权限:当父类中权限被设为私有的成员,子类无论由哪种继承方式都无法访问父类中私有权限的内容。当父类中非私有的成员被继承时,继承后的子类成员权限有以下规则,Min(访问限制符,继承方式),即继承后的子类成员权限取父类中访问限定符与继承方式的权限最小者。

继承方式
类成员/继承方式public(公共继承)protected(保护继承)private(私有继承)
基类public(公共权限)派生类对应权限为public派生类对应权限为protected派生类对应权限为private
基类protected(保护权限)派生类对应权限为protected派生类对应权限为protected派生类对应权限为private
基类private(私有权限)派生类无法访问派生类无法访问派生类无法访问

注意:父类中私有非静态属性被子类继承后虽然无法被子类访问,但子类依旧是继承下来了,只是没有访问权限罢了。

也可以通过sizeof运算符来验证这一点。

继承中构造和析构的顺序

        子类继承父类后,父子类间会有联系,当创建子类对象时既会调用子类的构造和析构函数,也会调用父类中的构造和析构,在这种情况下构造和析构的顺序会有分歧,下面来探讨一下这个问题。

继承中先调用父类构造在调用子类构造,析构顺序与构造正相反。

继承中同名成员的处理

          当子类存在与父类中同名的成员时,继承后会有一份同名的成员,此时可以运用域访问限定符来区分。操作如下:

结论:子类对象能直接访问到子类中的同名成员。子类对象加作用域可以访问到父类同名成员。当子类与父类拥有同名成员函数时,子类会隐藏父类中所有的同名成员函数,加作用域能访问到父类中的同名函数。

静态同名成员也可以通过域名直接访问,无需创建对象。

多继承

        多继承语法:class 子类 : 继承方式 父类1, 继承方式 父类2,...

 菱形继承问题

 

 如上图,当具有相同来源的子类被同一个孙类继承后,就会出现菱形继承的问题,主要的毛病是,造成数据的冗余访问二义性。

 

 虚继承

        在子类继承父类时,在继承方式前加上 virtual 关键字可以对父类进行虚继承,虚继承可以解决菱形继承时引发的数据冗余和二义性的问题。本文对于虚继承语法内部暂不做讲解,在实际项目中一般也不会写菱形继承的代码。下面展示一下虚继承后的菱形继承效果:

派生类向基类的转换

        public继承的派生类对象可以赋值给基类的指针/指针的引用。但将派生类对象赋值后,基类的指针或者引用并未存储属于派生类对象的成员属性,准确来说是基类仅得到属于自己继承给派生类的那一部分属性,我们也管这叫类型的切片或者切割。在后面提到的的多态中会大量使用这种类型切片的语法。

 以上是继承语法的所有内容了,从下面开始延续多态的讲解。


多态

        多态,简单来说就是多种形态。多态分为静态多态动态多态,本文重点讲动态多态。其中,静态多态包括且不仅限于函数重载、运算符重载,静态多态主要的特点是:编译时决定函数的地址。动态多态则是由虚函数派生类来实现的,动态多态的主要特点是:运行时才决定函数的地址


 

动态多态语法实现

        动态多态满足条件:1、有继承关系;2、子类重写父类的虚函数。

以上条件满足后一个多态就完成了,但如果不使用特定的方式来调用就无法体现多态的动态性,因此C++语法又提供了动态多态的使用条件:父类指针或引用执行子类对象对应的多态函数。

前面三点同时满足情况下才能验证动态多态的实现。

 以上是多态的体现,下面来展开讲讲。

第一:虚函数

        在C++中用关键字virtual来修饰后的函数被称为虚函数,其实在继承那部分的时候我们也提到用这个关键字来解决菱形继承的问题,即虚继承。有关虚函数的讲解暂时搁置一边,下文中的多态的底层原理会重点刨析有关虚函数的问题。现在只需要知道加了virtual关键字后的函数被设置为了虚函数就行。

第二:子类重写父函数的虚函数

         概念:函数名、返回值、参数类型完全一致的函数才能称为重写。子类是继承父类的,子类重写父类的虚函数后子类从父类中继承下来的虚函数就会被重写后的版本所覆盖。也就是能够有属于自己的独特的行为,虽然和父类名称相同、返回值相同、参数类型相同。同样重写后的底层有什么变化,我整理到下文的多态的底层原理这一专题会给大家讲解。

第三:父类指针或引用执行子类对象中对应的多态函数(虚函数且被重写了的函数称为多态函数)

以上是Chatgpt的回答,它的意思是说用父类指针或引用能够统一管理多个子类,即更贴切多态的概念和语法设计初衷,调用也不会存在歧义。在此之后我深入探究了一下有关这一问题它的答案如下:

 在此仅作兴趣了解即可,我们只要记住要使用多态就必须用父类指针指向子类对象,并用该指针调用对应的虚函数即可。

         以上是有关动态多态使用的理解,下面来剖析一下多态的底层原理


多态的底层原理

        不知道大家是否好奇动态多态中的动态究竟是体现在哪里?以及使用动态多态情况下明明用的是父类的指针,为何使用的是对应的子类的函数呢?还有就是当父类存在多个虚函数时,使用父类指针是如何精确调用到对应的虚函数的呢?下面来剖析多态的底层原理,以便打破大家对多态的恐惧之心。

         在C++中, 动态多态的实现主要依赖于虚函数表(vftable)虚函数表指针(vfptr),这是动态绑定的运行机制,下面围绕这两点来讲解多态的底层原理。

虚函数表(vftable)

        **虚函数表其实就是一个函数指针数组,如果C语言学得扎实的话不难理解,(对于函数指针数组不够清晰的同学可以查看我之前写的有关指针专题的文章),当一个类中包含虚函数时,编译器会为该类生成一个虚函数表。每个包含虚函数的类中都会生成独立的虚函数表,表中存储的是该类中虚函数的地址,多态的动态绑定其实就是在运行时遍历该函数指针数组找到对应虚函数地址的过程。

        **子类继承父类后,会自动生成一份地址独立于父类但所有内容与父类相同的虚函数表,当子类重写虚函数表时,子类的虚函数就会覆盖继承下来的那份虚函数表中的内容,以反映自身的函数实现。

虚函数表指针(vfptr)

        **虚函数表指针(也有地方叫虚指针),是一个隐藏在类对象中的指针,用于指向虚函数表,每个对象在创建时都会有一个虚指针,指向该对象所属的类的虚函数表。该指针用于遍历虚函数表来决定调用类中的哪个虚函数。就像前面语法实现那一专题代码图中sizeof计算的就是虚函数表指针的大小(X64环境)。

调用过程

        **当通过基类指针或引用来调用对应的虚函数时,编译器会使用对象的虚函数表指针(vfptr)找到虚函数表(vftable)中对应的虚函数地址,进而调用该函数的实现。

下图为以上文字的体现:

 以上就是多态的底层执行原理,理解完后才能真正掌握多态的使用,希望大家花点心思琢磨一下,我也用了两三天想才想明白多态的原理,真的很需要耐心!!!

纯虚函数与抽象类

纯虚函数:一般情况下多态父类中的虚函数实现都是没必要的,重要的是子类中具体虚函数的实现,一般也不会调用多态中的动物类(父类),因此在多态中的父类都是零实现,像这种在类中没有实现的虚函数被称为纯虚函数,类中包含有纯虚函数的类被称为抽象类(structure class)。

 注意:1、抽象类无法实例化对象

            2、如果子类没有重写父类中纯虚函数,子类中的继承下来的虚函数也是纯虚函数,子类也是抽象类。

虚析构与纯虚析构

首先观察以下代码

要讲清楚为什么要重写析构函数就必须搞清楚上面这段代码是否有内存泄漏,搞清楚这个问题才能给大家引入虚析构纯虚析构的问题。

        首先,前面给大家剖析了多态的底层原理与调用过程,我们知道虽然用的是父类指针来指向子类对象,但说到底这个指针就是父类创建的,红色区域内释放的就是父类指针指向的空间,因此只会调用父类的析构,无法调用子类的析构,因此存在内存泄露问题。

        为了单独解决这种情况,我们可以引入虚析构的概念,也就是说虚析构就是为了解决子类中存在动态开辟空间在删除时出现内存泄漏的问题。

        **与虚函数相同,只需要在父类析构函数前加上virtual关键字即可构成虚析构函数,如果父类不实现虚析构函数可以在后面直接加上=0即可构成纯虚析构函数,与虚函数不同的是:虚析构不需要再子类中重写,事实上只要父类中的析构被写成了虚析构后,所有继承该父类的子类,其对应的析构都已经是虚析构了,在释放父类指针内存时都是先调用子类的虚析构在调用父类的虚析构,此时内存泄露的问题也就解决了。

        **除此之外,纯虚析构并不是声明即可,纯虚析构即使不用也需要在类外定义。

如以下代码所示:

 纯虚析构版本如下:

 读至此处的同学,真的非常感谢!!!这篇文章我真的花了很大工夫,整理了很多也想得很深,创作此文前一边看语法一边思考,实属不易,现在突然顿悟想明白之后真的很有成就感,因此希望在这篇文章中将思考过程也总结一遍。希望对大家有所收获,也希望大家不碍点个三连,非常感谢。

附上封面

相关文章:

C++类与对象-继承和多态(超全整理)

前言 前面讲类与对象上中下时,所讲的都是在单个类中相关的语法(初始化列表、this指针、静态成员、常函数和常对象......)或者使两个不同的类产生联系的语法(友元)。而本文虽然也是类与对象的内容,但和之前的…...

3.3 Thymeleaf语法

文章目录 引言Thymeleaf标签显示标签链接地址标签条件判断标签元素遍历标签 Thymeleaf表达式变量表达式选择变量表达式消息表达式链接表达式 Thymeleaf内置对象上下文对象上下文变量上下文区域请求对象响应对象会话对象日期对象 实战演练创建控制器创建模板页面 结语 引言 Thy…...

使用Dlib库实现人脸检测和关键点定位

目录 前言 一、安装Dlib库 二、人脸检测 三、人脸关键点定位 前言 Dlib是一个现代化的 C 工具包,提供了一些机器学习算法和工具,特别是在面部识别和人脸关键点检测方面非常流行。它具有易于使用的 Python 接口,并被广泛应用于计算机视觉项…...

DNS隧道流量分析

DNS隧道 DNS协议又称域名系统是互联网的基础设施,只要上网就会用到,因而DNS协议是提供网络服务的重要协议,在黑客进入内网后会使用DNS、ICMP、HTTP等协议隧道隐藏通信流量。本文通过DNS隧道实验并对流量进行分析,识别DNS隧道流量…...

HCIP-HarmonyOS Application Developer 习题(十一)

(填空)1、某开发者在使用HarmonyOs的分布式力时,分布式_____能力是其他分布式能力的基础。 答案:软总线 分析:分布式软总线是手机、平板、智能穿戴、智慧屏、车机等分布式设备的通信基座,为设备之间的互联互…...

使用Ollama测试OpenAI的Swarm多智能体编排框架

Ollama https://ollama.com/ ollama run qwen2.5Install Requires Python 3.10 pip install githttps://github.com/openai/swarm.git代码V1 # 导入Swarm和Agent类 from swarm import Swarm, Agent from openai import OpenAI # 实例化Swarm客户端 openai_client OpenAI…...

C# 完美操作 Active Directory 详细总结,轻松玩转域管理

前言 嗨,大家好! 在这个数据信息飞速发展的 21 世纪,数据安全成为了每个企业关注的焦点,保护企业数据安全日益成为企业工作中的重中之重。 域服务器,尤其是微软的 Active Directory(AD)&…...

PCL 点云配准 KD-ICP算法(精配准)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 加载点云函数 2.1.2 构建KD树函数 2.1.3 KD-ICP配准函数 2.1.4 点云可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接: PCL点云算法…...

uniapp打包安卓apk步骤

然后安装在手机上就可以啦...

Springboot 整合 Java DL4J 实现安防监控系统

🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,…...

【数据结构与算法】第1课—算法复杂度

文章目录 1. 数据结构2. 算法3. 算法效率4. 算法复杂度5. 算法时间复杂度5.1 大O的渐进表示法5.2 时间复杂度示例 6. 空间复杂度6.1 练习16.2 练习26.3 练习3 1. 数据结构 数据结构是计算机存储、组织数据的方式,指相互之间存在一种和多种特定关系的数据元素的集合&…...

利用高德API获取整个城市的公交路线并可视化(五)

如果说我比别人看得更远些,那是因为我站在了巨人的肩上。——牛顿 参考:使用高德API获取公交线路数据,无需代码_实时公交api-CSDN博客 记录于2024年10月,因数据获取受网站更新策略等影响可能会失效,故记录写作时间,同时拾人牙慧,优化了后半部分数据直接导出为csv和shp…...

DNS:互联网域名系统的核心

什么是 DNS? DNS(Domain Name System,域名系统)是互联网的一项基础服务,它负责将人类容易记忆的域名(如 www.example.com)转换成计算机可以识别的 IP 地址(如 192.0.2.1&#xff09…...

小猿口算炸鱼脚本

目录 写在前面: 一、关于小猿口算: 二、代码逻辑 1.数字识别 2.答题部分 三、代码分享: 补充:软件包下载 写在前面: 最近小猿口算已经被不少大学生攻占,小学生直呼有挂。原本是以为大学生都打着本…...

浅谈云原生--微服务、CICD、Serverless、服务网格

往期推荐 浅学React和JSX-CSDN博客 一文搞懂大数据流式计算引擎Flink【万字详解,史上最全】-CSDN博客 一文入门大数据准流式计算引擎Spark【万字详解,全网最新】_大数据 spark-CSDN博客 目录 1. 云原生概念和特点 2. 常见云模式 3. 云对外提供服务的…...

android app执行shell命令视频课程补充android 10/11适配-千里马android

(https://blog.csdn.net/learnframework/article/details/120103471) https://blog.csdn.net/learnframework/article/details/120103471 hi,有学员在学习跨进程通信专题课程时候,在实战app执行一个shell命令的项目时候,对课程本身的android …...

C++笔记-UTF8和UTF8-dom的区别

在文件格式上,UTF-8 和 UTF-8-BOM 是两种不同的编码方式,其中 UTF-8-BOM 包含字节顺序标记(BOM),而 UTF-8 则不包含。 UTF-8: UTF-8 是一种以字节为单位的可变长度字符编码,常用于以字节为单位…...

“探索Adobe Photoshop 2024:订阅方案、成本效益分析及在线替代品“

设计师们对Adobe Photoshop这款业界领先的图像编辑软件肯定不会陌生。如果你正考虑加入Photoshop的用户行列,可能会对其价格感到好奇。Photoshop的价值在于其强大的功能,而它的价格也反映了这一点。下面,我们就来详细了解一下Adobe Photoshop…...

网页复制粘贴助手,Chrome网页复制插件(谷歌浏览器复制插件)

一款解决网页限制复制问题的插件,当你遇到限制复制粘贴和右键的网页是不是很头痛?安装这个插件后,点下插件按钮就能解决了 碰到这种情况 也是非常头疼 chrome拓展-chrome插件-强制复制 当我们浏览网页的时候,看到感兴趣的内容就…...

【C++刷题】力扣-#118-杨辉三角

题目描述 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。在杨辉三角中,每个数是它正上方两个数的和。 示例 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]]题解 这个问题…...

Linux下的环境变量

目录 1.引言 1.1bash的部分工作 1.2main函数也有参数 1.3我们可以通过给main函数传入不同的参数,让同一份代码实现不同的功能 1.4先认识一个环境变量PATH,帮助Linux找到指令程序的地址 2.环境变量 2.1环境变量的概念 2.2见见其他的环境变量 2…...

Edge论文的创新点

创新点及其来源 1. 从灰度边缘重建RGB图像的方法(EdgRec) 基于的方法:传统的重建方法,如使用自动编码器或生成模型来重建正常样本的图像,并通过对原始图像和重建图像的比较来检测异常。 重建过程: 训练阶…...

‌ComfyUI 高级实战:实现华为手机的AI消除功能

大家好,我是每天分享AI应用的萤火君! 不知道大家是否还记得华为 Pura 70的「AI消除」事件,当时使用 华为Pura 70 系列手机的智能消除功能时,该功能可以被用来消除照片中女性胸口处的衣物,这一功能曾引发广泛的关注和伦…...

我记得我曾喜欢过冬天

写在前面 1316 字 | 感触 | 世界 | 情感 | 体验 | 经历 | 想法 | 认知 正文 晚上出门,起电单车,很冷。冻得有些发抖。下车,我第一时间和珍发了消息。 我说,居然在四川感受到了哈尔滨的温度。 哈尔滨的夏天很热,但哈尔…...

最新夜间数据集发布LoLI-Street: 33000帧数据,涵盖19000个目标

最新夜间数据集发布LoLI-Street: 33000帧数据,涵盖19000个目标 Abstract 低光照图像增强(LLIE)对于许多计算机视觉任务至关重要,包括目标检测、跟踪、分割和场景理解。尽管已有大量研究致力于提高在低光照条件下捕捉的低质量图像…...

反向传播算法与随机搜索算法的比较

反向传播算法与随机搜索算法的比较 在这篇文章中,我们将通过一个简单的线性回归问题来比较反向传播算法和随机搜索算法的性能。我们将使用Python代码来实现这两种算法,并可视化它们的梯度下降过程。 反向传播算法 反向传播算法是深度学习和神经网络训…...

【PDF文件】默认被某种软件打开,如何进行修改?

当有时下载某种软件后,电脑中的PDF文件就默认由该种软件打开,每次需要右键选择打开方式才能选择需要的其他软件打开。如下图所示。 修改方法: (1)点击电脑的“设置”,选择应用 (2)…...

Kaggle Python练习:字符串和字典(Exercise: Strings and Dictionaries)

文章目录 问题:搜索特定单词并定位思路代码实现官方代码代码解析 更进一步 问题:搜索特定单词并定位 一位研究人员收集了数千篇新闻文章。但她想将注意力集中在包含特定单词的文章上。完成以下功能以帮助她过滤文章列表。 您的函数应满足以下条件&…...

React(四) 事件总线,setState的原理,PureComponent优化React性能,ref获取类组件与函数组件

文章目录 一、全局事件总线二、setState的原理1. 为什么要使用setState修改数据2. setState的三种用法(1) 基本使用(2) 传入回调函数(3) setState是一个异步调用 3. setState为什么要设置成异步 二、PureComponent优化性能1. React的diff算法以及Key的优化(扩展)(1) diff算法(2…...

Java学习-JVM

目录 1. 基本常识 1.1 JVM是什么 1.2 JVM架构图 1.3 Java技术体系 1.4 Java与JVM的关系 2. 类加载系统 2.1 类加载器种类 2.2 执行顺序 2.3 类加载四个时机 2.4 生命周期 2.5 类加载途径 2.6 双亲委派模型 3. 运行时数据区 3.1 运行时数据区构成 3.2 堆 3.3 栈…...