C++::多态

目录

一.多态的概念

二.多态的定义及实现

二.1多态的构成条件

二.2虚函数

1.虚函数的写法

2.虚函数的重写/覆盖

3.协变

二.3析构函数的重写

二.4override和final关键字

​编辑二.5重载/重写/隐藏的对比

三.多态的运行原理(一部分)

四.多态的常见问题!!!!


一.多态的概念

多态(polymorphism)的概念:通俗来说,就是多种形态多态分为编译时多态(静态多态)和运⾏时多 态(动态多态),这里我们重点讲运行时多态,编译时多态(静态多态)和运⾏时多态(动态多态)。编译时 多态(静态多态)主要就是函数重载和函数模板,他们传不同类型的参数就可以调用不同的 函数,通过参数不同达到多种形态,之所以叫编译时多态,是因为他们实参传给形参的参数匹配是在 编译时完成的,我们把编译时⼀般归为静态,运行时归为动态。 运行时多态,具体点就是去完成某个个为(函数),可以传不同的对象就会完成不同的⾏为,就达到多种 形态。比如买票这个行为,当普通⼈买票时,是全价买票;学生买票时,是优惠买票(5折或75折);军 ⼈买票时是优先买票。再比如,同样是动物叫的⼀个行为(函数),传猫对象过去,就是”(>^ω^<)喵喵“,传狗对象过去,就是”汪汪“。

二.多态的定义及实现

二.1多态的构成条件

多态是⼀个继承关系的下的类对象,去调用同⼀函数,产生了不同的行为。

实现多态还有两个必须重要条件:必须是基类的指针或者引用调用虚函数 被调用的函数必须是虚函数,并且完成了虚函数重写/覆盖

说明:要实现多态效果,第⼀必须是基类的指针或引用,因为只有基类的指针或引用才能既指向基类 对象又指向派生类对象;第⼆派生类必须对基类的虚函数完成重写/覆盖,重写或者覆盖了,基类和派生类之间才能有不同的函数,多态的不同形态效果才能达到

示例

二.2虚函数

虚函数是构成多态的必要条件,那么怎样写虚函数,才能够正确实现多态呢?

1.虚函数的写法

类成员函数前面加virtual修饰,那么这个成员函数被称为虚函数。注意非成员函数不能加virtual修饰。(友元函数不能加virtual,因为友元函数不是成员函数)(stastic也不能和virtual同时使用

2.虚函数的重写/覆盖

虚函数的重写/覆盖:派生类中有⼀个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值 类型、函数名字、参数列表完全相同,后续我们将其称之为三同)(注意!!函数的缺省值可以不同!!!),称派生类的虚函数重写了基类的虚函数。

注意:

1.在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承 后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范不建议这样使用不过在考试选择题中经常会故意买这个坑让你判断是否构成多态。

2.注意!!构成多态时可以看作使用被继承的类的函数头+自己的实现,所以如果两个虚函数的缺省值不同,缺省值用的是被继承的类的,构成重写时不要求缺省值相同,形参的名字也可以不同

示例:

上述代码在构成多态时,虽然调用的是子类的方法,但是由于使用的是父类的函数头,所以缺省值为1,输出的是B->1

3.协变

正常情况下,我们实现多态需要满足三同,派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。协变的实际意义并不大,所以我们了解⼀下即可。

二.3析构函数的重写

基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析 构函数构成重写,虽然基类与派生类析构函数名字不同看起来不符合重写的规则,实际上编译器对析 构函数的名称做了特殊处理,编译后析构函数的名称统⼀处理成destructor,所以基类的析构函数加了 vialtual修饰,派生类的析构函数就构成重写。

 下面的代码我们可以看到,如果~A(),不加virtual,那么delete p2时只调用的A的析构函数,没有调用B的析构函数,就会导致内存泄漏问题,因为~B()中在释放资源。

(只要这个类想构成多态,那么就把父类的析构设置成虚函数,否则肯能会造成析构调用错误,可能会发生内存泄漏的情况)

 

二.4override和final关键字

C++对虚函数重写的要求比较严格,但是有些情况下由于疏忽,比如函数名写错参数写错等导致⽆法构成重写,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此C++11提供了override,可以帮助用户检测是否重写。如果我们不想让派生类重写这个虚函数,那么可以用final去修饰。

注意,这两个虚函数的函数名是不一样的,所以他们不构成函数重写,有了override检查后,发生报错

二.5重载/重写/隐藏的对比

二.6纯虚函数和抽象类

在虚函数的后面写上=0,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被 派⽣类重写,但是语法上可以实现),只要声明即可。包含纯虚函数的类叫做抽象类,抽象类不能实例 化出对象,如果派生类继承后不重写纯虚函数,那么派生类也是抽象类。纯虚函数某种程度上强制了 派生类重写虚函数,因为不重写实例化不出对象。

注意:派生类必须重写纯虚函数,否则,认为这个类也是抽象类,无法实例化出对象

三.多态的运行原理(一部分)

虚函数内存对齐时会多一个虚表指针,存的是虚函数的指针,大小和机器位数有关,注意,有多个虚函数时,虚表指针的大小不会改变,依旧是地址的大小,因为虚表指针的底层是一个指针数组,它指向这个指针数组,准确的说,是虚函数指针数组

虚函数被重写之后,虚表指针会覆盖,(重写也叫做覆盖)重写是语法层面的表达,覆盖是原理层面的表达

为什么多态能够实现?首先,需要满足是一个父类的指针或者引用调用,其次,要满足虚函数的重写,重写虚函数时,虚表指针会被覆盖,虽然接收到的是一个父类的对象,实际上那是一个被切片过的子类,虚表函数是被覆盖过的,运行时,去虚表函数找到函数的地址进行调用,就实现了多态

那么为什么传一个父类的值不可以实现多态呢?传值给父类,虽然是切片,但是系统还是认为他是一个父类,用的是父类的虚表(不同类型的虚表是不一样的,切片不会传递虚表),无法实现多态,只有指针或者引用才能实现既可以指向父类,又可以指向子类,指向谁就调用谁的虚表

四.多态的常见问题!!!!

1.static与virtual不能同时使用

2.virtual关键字只在声明时加上,在类外实现时不能加

3.友元函数不属于成员函数,不能成为虚函数

4.静态成员函数就不能设置为虚函数静态成员函数与具体对象无关,属于整个类,核心关键是没有隐藏的this指针,可以通过类名::成员函数名 直接调用,此时没有this无法拿到虚表,就无法实现多态,因此不能设置为虚函数

5.在编译期间,通过传递不同类的对象,编译器选择调用不同类的虚函数(是错的!!!)不是在编译期,而应该在运行期间,编译期间,编译器主要检测代码是否违反语法规则,此时无法知道基类的指针或者引用到底引用那个类的对象,也就无法知道调用那个类的虚函数。在程序运行时,才知道具体指向那个类的对象,然后通过虚表调用对应的虚函数,从而实现多态。

6.多继承的时候,就会可能有多张虚表

7.父类对象的虚表与子类对象的虚表没有任何关系,这是两个不同的对象

8.形成多态时,即使子类中的虚函数是私有的,也可以直接调用,但是父类的虚函数不能是私有的

在父类还没有构造出来时,不满足多态的条件,不能形成多态!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能

法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 &#xff0c;该模型凭借240亿参数的轻量级设计&#xff0c;在多项基准测试中表现优异&#xff0c;甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…

从零开发数据可视化

一、可视化模版展示 二、知识及素材准备 div css 布局flex布局Less原生js jquery 的使用rem适配echarts基础 相关js、images、font百度网盘下载链接&#xff1a; 通过百度网盘分享的文件&#xff1a;素材1 链接: https://pan.baidu.com/s/1vmZHbhykcvfLzzQT5USr8w?pwdwjx9…

WSL git文件异常 所有文件均显示已修改

如图&#xff0c;文件中没有任何修改&#xff0c;但是都显示多了一个^M 原因&#xff1a;是因为在Windows系统中git clone的文件夹&#xff0c;在WSL中会显示冲突。 解决方案&#xff1a;删掉之前在windows下git clone的文件夹&#xff0c; 然后在WSL中重新git clone

基于STM32进行FFT滤波并计算插值DA输出

文章目录 一、前言背景二、项目构思1. 确定FFT点数、采样率、采样点数2. 双缓存设计 三、代码实现1. STM32CubeMX配置和HAL库初始化2. 核心代码 四、效果展示和后话五、项目联想与扩展1. 倍频2. 降频3. 插值3.1 线性插值3.2 样条插值 一、前言背景 STM32 对 AD 采样信号进行快…

ENSP学习day9

ACL访问控制列表实验 ACL&#xff08;Access Control List&#xff0c;访问控制列表&#xff09;是一种用于控制用户或系统对资源&#xff08;如文件、文件夹、网络等&#xff09;访问权限的机制。通过ACL&#xff0c;系统管理员可以定义哪些用户或系统可以访问特定资源&#x…

Ubuntu22.04通过DKMS包安装Intel WiFi系列适配器(网卡驱动)

下载驱动包 访问 backport-iwlwifi-dkmshttps://launchpad.net/ubuntu/source/backport-iwlwifi-dkms 网站&#xff0c;找到适用于Ubuntu 22.04的update版本&#xff08;如backport-iwlwifi-dkms_xxxx_all.deb&#xff09;&#xff0c;下载至本地。 安装驱动 在下载目录中执行以…

c#难点整理2

1.对象池的使用 就是先定义一系列的对象&#xff0c;用一个&#xff0c;调一个。 public class ObjectPool<T> where T : new(){private Queue<T> pool; // 用于存储对象的队列private int maxSize; // 对象池的最大容量// 构造函数public ObjectPool(int maxSi…

音频录制小妙招-自制工具-借助浏览器录一段单声道16000采样率wav格式音频

先看效果 1、打开页面 2、点击开始录音&#xff0c;弹出权限提示&#xff0c;点击“仅这次访问时允许” 3、录完后&#xff0c;点击停止 4、文件自动下载到默认目录 上代码 js 部分 document.addEventListener(DOMContentLoaded, () > {const startBtn document.getEleme…

C++:背包问题习题

1. 货币系统 1371. 货币系统 - AcWing题库 给定 V 种货币&#xff08;单位&#xff1a;元&#xff09;&#xff0c;每种货币使用的次数不限。 不同种类的货币&#xff0c;面值可能是相同的。 现在&#xff0c;要你用这 V 种货币凑出 N 元钱&#xff0c;请问共有多少种不同的…

Python设计模式 - 适配器模式

定义 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它用于将一个类的接口转换为客户端所期待的另一个接口。 注&#xff1a;在适配器模式定义中所提及的接口是指广义的接口&#xff0c;它可以表示一个方法或者一组方法的集合。 结构 …

Word中公式自动标号带章节编号

&#xff08;1&#xff09;插入一行三列的表格&#xff0c;设置宽度分别为0.5&#xff0c;13.39和1.5&#xff0c;设置纵向居中&#xff0c;中间列居中对齐&#xff0c;最右侧列靠右对齐&#xff0c;设置段落如下 &#xff08;2&#xff09;插入域代码 【Word】利用域代码快速实…

OSASIS(One-Shot Structure-Aware Stylized Image Synthesis)

文章目录 摘要abstract论文摘要方法损失函数实验结论 总结 摘要 本周阅读了一篇关于新型图像风格化的论文《One-Shot Structure-Aware Stylized Image Synthesis》&#xff0c;旨在解决现有GAN模型在风格化过程中难以保持输入图像结构的问题。通过分离图像的结构和语义信息&am…

优先队列 priority_queue详解

说到&#xff0c;priority_queue优先队列。必须先要了解啥是堆与运算符重载(我在下方有解释)。 否则只知皮毛&#xff0c;极易忘记寸步难行。 但在开头&#xff0c;还是简单的说下怎么用 首先&#xff0c;你需要调用 #include <queue> 在main函数中&#xff0c;声明…

Matplotlib

一、Matplotlib快速入门 学习目标 了解什么是matplotlib 为什么要学习matplotlib matplotlib简单图形的绘制 1、什么是Matplotlib 是专门用于开发2D图表(包括3D图表) 以渐进、交互式方式实现数据可视化 2、为什么要学习Matplotlib 可视化是在整个数据挖掘的关键辅助工…

【leetcode hot 100 131】分割回文串

解法一&#xff1a;回溯法动态规划法 回溯法&#xff1a; 假设我们当前搜索到字符串的第 i 个字符&#xff0c;且 s[0…i−1] 位置的所有字符已经被分割成若干个回文串&#xff0c;并且分割结果被放入了答案数组 ans 中&#xff0c;那么我们就需要枚举下一个回文串的右边界 j…

ToDesk云电脑各类鼠标有什么区别?虚拟/3D/游戏鼠标等各有利

不知道各位在使用ToDesk云电脑的时候是否是有注意到&#xff0c;这其中的鼠标竟有多种名称、多种模式可以选&#xff0c;比如锁定鼠标、3D鼠标、游戏鼠标这几项。 那么这些不同名称的鼠标都代表什么意思呐&#xff0c;又应该怎么选择、怎么用呐&#xff1f;本篇内容小编就为大…

手机怎么换网络IP有什么用?操作指南与场景应用‌

在数字化时代&#xff0c;手机已经成为我们日常生活中不可或缺的一部分&#xff0c;无论是工作、学习还是娱乐&#xff0c;手机都扮演着至关重要的角色。而在手机的使用过程中&#xff0c;网络IP地址作为设备在互联网上的唯一标识符&#xff0c;其重要性和作用不容忽视。本文将…

Bulk Rename Utility(BRU)——大批量重命名实用程序

Bulk Rename Utility&#xff08;BRU&#xff09;——大批量重命名实用程序 博主要给博客网站搞博客封面&#xff0c;几百张图没编号&#xff0c;一弄这个就好了&#xff0c;亲测十分好用&#xff0c;下面的b站教程更是一绝&#xff0c;快快使用起来 文章目录 Bulk Rename Ut…

鸿蒙生态开发

鸿蒙生态开发概述 鸿蒙生态是华为基于开源鸿蒙&#xff08;OpenHarmony&#xff09;构建的分布式操作系统生态&#xff0c;旨在通过开放共享的模式连接智能终端设备、操作系统和应用服务&#xff0c;覆盖消费电子、工业物联网、智能家居等多个领域。以下从定义与架构、核心技术…