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

【C++】多态(上)超详细

封装,继承,多态不只是C++的三大特性,而是面向对象编程的三大特性。

什么是多态:

不同的对象做同一件事情,结果会出现多种形态。

1.满足多态的几个条件

1.父子类完成虚函数重写(需要满足三同:函数名,参数,返回值都需要相同)。

2.父类的指针或引用去调用虚函数。(指向谁就调用谁的虚函数)

既然有了父类和子类,那么说明多态发生的前提是继承。

例子:

不同身份的人买票的价格是不同的。

但如果不满足多态的条件,就达不到我们想要的结果:

2.多态的坑

2.1虚函数重写的两个例外

强调一下:

1.返回值只能是父类返回父类的指针或引用,子类返回子类的指针或引用,顺序不能反过来。

2.返回值可以是其他不相关的父子类,也可以是自己的父子类。

关于析构函数的重写

如果我们正常的写析构函数,看看它们调用的情况:

可见,两次析构都调用的是基类的,这是正常现象,两个Person的指针自然调用Person的析构。但这并不是我们的目的,我们想要的是指针指向谁就应该调用谁的析构,也就是说:我们想让P2调用Student的析构,只有这样才满足多态的规则。

但如果我们用virtual修饰这两个析构函数呢:

加上virtual就达到了我们的目的,也就是说父子类的析构函数构成虚函数重写。

但有些奇怪,这两个析构函数的名字明明不同,不符合多态的语法,为什么依然可以构成重写呢?

其实这里编译器会把析构函数的名字全部换成destructor,这样就满足多态的语法了!

那C++的语法为什么不把析构函数的名字直接定义成destructor,而是私下换名字呢?这样不麻烦嘛?

其实这也是无奈之举,因为析构函数的概念早于多态,在多态之前已经把析构函数的名字设计好了,祖师爷也没想到后面设计多态的语法时会在这个地方有坑,只能自己私下改名字了。

结论:建议把析构函数写成虚函数,防止内存泄漏的发生。

其实还有一个例外,就是派生类可以不写virtual。

这也是C++语法常常被吐槽的点,但还是建议写上,不然容易被人吐槽。

2.2一道杀人诛心的面试题

这道题目曾被多家大型公司(百度,腾讯等)当作面试题。

先说答案:B。是不是有点匪夷所思?

思路:

继承只是一个形象的说法,实际上在继承时并没有把父类的成员拷贝到子类中,而是用了一套查找规则:在P指针调用父类函数时,编译器会先在子类中找,没找到再去父类去找,所以父子类的同名函数会构成隐藏。

所以在给test函数传this指针时,this的类型是A*。但用this调用func时依然构成多态调用,因为this依然指向的是B类型,所以会直接调B中的func。

下面就到这个题目特别坑的点:多态的虚函数重写,重写的只是函数体的实现。意思就是:

子类的func是对父类func的重写,但只是重写了函数体的实现,函数的结构部分依然用父类的。所以val的值是1。

那咱们回过头来想,既然子类的函数体结构部分没有调用,那可以省略virtual好像也有些道理。

3.关键字override和final

3.1final

如果让你设计一个不能被继承的类,其实有两种方法。

方法一:

把基类的构造函数定义为私有。(C++98)

原因是:基类的构造函数在派生类中不可访问,那么派生类就无法对象实例化。

方法二:

利用关键字final。(C++11)

方法二很简单,就是用final修饰基类之后就无法继承了。

3.2override

override是加到派生类的重写虚函数中,用于检查是否完成重写。

成功重写时,是没有任何报错的。

没有成功重写就会报错,所以我们在写代码时尽量把这个关键字加上。

4.对比重载/重写/隐藏

5.多态的底层

5.1虚函数表指针

这道题的答案是12。因为Base类中有一个虚函数,所以在成员对象中就会多出一个指针,叫虚函数表指针,简称虚表指针。

这个指针指向了一张表,这个表里存放了虚函数的指针。

在x86平台下:

通过对比监视窗口和内存窗口可以看到在地址0x00E17B34位置存放了00e112df指针,在地址0x00E17B38位置存放了00e1124e指针。

5.2虚函数表指针的作用

下面我们来看一看编译器是如何通过虚表指针实现多态的。

这是Mike的对象模型和监视窗口:

这是John的对象模型和监视窗口:

通过对比Mike和John的对象模型发现:

在John的对象模型中004a9b54和下面的1是继承Mike的,1下面的2是John的成员变量。

虽然是继承下来的但有些不一样:

继承下来的虚表指针和Mike的虚表指针不一样,一个是004a9b34一个是004a9b54。

既然虚表指针不一样,那虚表指针里面的函数指针也应该不一样,观察监视窗口,我们发现Mike的虚表指针中存放的是Person的BuyTickt函数,John的虚表中存放的是Student的BuyTickt函数。

所以,多态的实现过程就是通过虚表指针!当Student对象传给Person对象时,通过切片把虚表指针切过去,然后通过Student的虚表指针调用Student的虚函数。当Person对象传给Person对象时,通过Person的虚表指针调用Person的虚函数。

但是当不满足多态语法时,编译器先检查,如果不满足多态语法,编译器就直接通过对象类型去调用成员函数,就不会通过虚表指针调用了。

补充一下:

每个对象都有一张虚表,同类型的对象共用一张虚表,不同类型的对象虚表不同。

6.单继承中的虚表

Derive继承Base后,通过监视窗口查看它们的虚表发现:Base的虚表是正常的里面有两个函数指针Func1和Func2。但是Derive的虚表有问题,有Derive的Func1和Base的Func2。

其实在继承后,派生类的虚表可以形象的说:把基类的虚表拷贝下来,如果构成重写,那么派生类的重写的函数把基类的覆盖掉。所以Func1是Derive的,Func2是Base的。

但问题是Func3和Func4哪里去了呢?

这也是VS的bug,其实VS的监视窗口有些时候并不准确,还要去内存窗口看一下!

通过对比监视窗口和内存窗口,我们发现Derive的虚表中好像存了4个函数指针:

前两个002b1410和002b1389和监视窗口中的一致,但后两个还不能确定,只能说比较像,所以我们需要写一个程序来验证我们的猜想。

验证结果:Derive的虚表中存了4个函数指针!

解释一下这个程序,写这个程序要求对指针的理解程度极高,如果你能看懂这个程序那么你在C语言指针方面的掌握非常好,如果能写出这个程序,那么你对指针的理解已经达到优秀了!

首先,虚表本质上是一个函数指针数组。我们平时传参传数组时,C语言考虑到效率问题往往传的是首元素的地址,比如一个int型的数组传参时传int*的指针。那么虚表中存放的全是函数指针,所以我们传参的时候应该传函数指针的地址,也就是二级指针。

那这个二级指针如何获得呢?在C语言部分,大家都知道,数组名就是首元素的地址,所以我们只需要得到虚表的名字(是d的成员变量)就可以了,也就是Derive d中的前4个字节。

难道将d强制类型转换成int就可以了嘛?这是不可以的,两个完全不想关的两个类型是不能强转的。这里给大家总结一下:1.int和float可以互相强转(char本质上也属于int型)2.任何类型的指针都可以相互强转 3.任何类型的指针可以和int相互强转。

知道这个知识后就可以取d的地址,前强转成int*,再解引用,这样就拿到了d的前4个字节,但这4个字节的类型是int,它真正的类型应该是虚表中首元素的地址,也就是Func1的指针的地址,强转过去后直接函数传参,然后采用数组下标的方式变量整个虚表就可以访问到虚表中所有的函数指针,然后再通过函数指针调用对应的函数就可以确认猜想!

相关文章:

【C++】多态(上)超详细

封装,继承,多态不只是C的三大特性,而是面向对象编程的三大特性。 什么是多态: 不同的对象做同一件事情,结果会出现多种形态。 1.满足多态的几个条件 1.父子类完成虚函数重写(需要满足三同:函…...

【Git】 Git分支操作指南

隐形的纪念躲在心里面 也许吧 也许不会再见 阴天或晴天 一天又一年 风它在对我说莫忘这一切 🎵 蔡淳佳《隐形纪念》 Git是一种非常强大的分布式版本控制系统,允许用户在开发过程中创建不同的分支(branch)来分…...

智慧文旅赋能旅游服务升级:以科技创新驱动行业变革,打造智慧化、个性化、高效化的旅游新体验,满足游客日益增长的多元化需求

目录 一、引言 二、智慧文旅的概念与内涵 三、智慧文旅在旅游服务升级中的应用 1、智慧旅游服务平台建设 2、智慧景区管理 3、智慧旅游营销 四、智慧文旅推动旅游行业变革的案例分析 案例一:某智慧旅游城市建设项目 案例二:某景区智慧化改造项目…...

AtCoder Beginner Contest 310 E题 NAND repeatedly

E题:NAND repeatedly 标签:动态规划题意:给定一个长度为 n n n的 01 01 01字符串 A i A_i Ai​,给定规则: 0 ⊼ 0 1 , 0 ⊼ 1 1 , 1 ⊼ 0 1 , 1 ⊼ 1 0 0⊼01,0⊼11,1⊼01,1⊼10 0⊼01,0⊼11,1⊼01,1⊼10。 求 ∑…...

一款简易的免费抽奖软件

一、介绍 这款抽奖软件设计简洁,操作便捷。用户可以轻松将参与名单通过EXCEL文件导入至程序中,并可根据需要设定各类奖品和对应的中奖人数。在选定了奖品后,用户只需点击“开始”按钮,随后再按下“暂停”按钮,软件便会…...

Kubernetes 监控管理

目录 1. Metrics Server2. Prometheus & Grafana3. cAdvisor4. 日志收集5. 告警与通知6. 最佳实践 Kubernetes 监控管理是确保集群稳定运行和应用服务质量的关键环节。它涉及收集、聚合、分析集群及其上运行的应用程序的各种指标和日志数据。 1. Metrics Server 作用&…...

哈希表第6/9题--四数相加II

题目描述&#xff1a; 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1&#xff1a; 输入&…...

使用JavaScript将富文本HTML转换为纯文本

在Web开发中&#xff0c;我们经常需要处理HTML内容&#xff0c;但有时为了特定的目的&#xff0c;比如文本处理、搜索或显示在非HTML环境中&#xff0c;我们可能希望将富文本HTML转换为纯文本。这里&#xff0c;我们将探讨如何使用JavaScript来实现这一功能。 为什么要将HTML转…...

2024-05-13 问AI: 介绍一下 google wavenet 声码器

文心一言 Google的WaveNet声码器是一个深度学习模型&#xff0c;用于生成高质量的音频信号&#xff0c;特别是人类语音。与传统的声码器相比&#xff0c;WaveNet可以生成更加自然和流畅的音频&#xff0c;因为它直接模拟了原始音频信号的波形生成过程。 WaveNet的核心思想是使…...

当代 Qt 正确的 安装方法 及 多版本切换

此文写于 20240511 首先去网站Index of /official_releases/online_installers下载一个安装器 安装器有什么用? 可以浏览安装版本 安装组件 安装器版本越能 能装的东西越多 现在只能选Qt5 和 Qt6 至于你公司用的Qt4 我也没招 见招时再拆招 安装器 默认国外源 可以换国内…...

matlab使用教程(70)—修改坐标区属性

1.控制坐标轴长度比率和数据单位长度 您可以控制 x 轴、y 轴和 z 轴的相对长度&#xff08;图框纵横比&#xff09;&#xff0c;也可以控制一个数据单位沿每个轴的相对长度&#xff08;数据纵横比&#xff09;。 1.1图框纵横比 图框纵横比是 x 轴、y 轴和 z 轴的相对长度。默认…...

手撕C语言题典——反转链表

目录 前言 一.思路 1&#xff09;创建新链表 2&#xff09;创建三个指针 二.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识&#xff0c;我们来尝试做一下关于顺序表的经典算法题~ 前言 反转…...

用lobehub打造一个永久免费的AI个人助理

Lobe Chat是一个开源的高性能聊天机器人框架&#xff0c;它被设计来帮助用户轻松创建和部署自己的聊天机器人。这个框架支持多种智能功能&#xff0c;比如语音合成&#xff08;就是让机器人能说话&#xff09;&#xff0c;还能理解和处理多种类型的信息&#xff0c;不仅限于文字…...

Linux网络编程】传输层中的TCP和UDP(UDP篇)

【Linux网络编程】传输层中的TCP和UDP&#xff08;UDP篇&#xff09; 目录 【Linux网络编程】传输层中的TCP和UDP&#xff08;UDP篇&#xff09;传输层再谈端口端口号范围划分认识知名端口号netstatiostatpidofxargs UDP协议UDP协议端格式UDP的特点面向数据报UDP的缓冲数据UDP使…...

Ciphey无法安装的解决办法

安装过程纯属自己实践&#xff0c;满满干货 困扰我几天的问题终于解决了 我看着教程在window上安装 python3.8/python3.9/python3.10无论如何都安装不上&#xff0c; 在win10虚拟机仍然安装不上 可能是我电脑环境问题 解决办法&#xff1a; 在kali中安装&#xff0c;但是…...

交互之舞:Processing中的用户互动与响应设计

前言&#xff1a; &#x1f31f;在前两篇文章中&#xff0c;我们已经学会了如何绘制静态图形和创建动态动画。今天&#xff0c;我们将迈入一个新的领域——交互设计。在Processing中&#xff0c;用户互动是创造沉浸式体验的关键。让我们一起探索如何让用户与你的艺术作品互动&…...

unetr_plus_plus(UNETR++、nnU-Net)系列数据处理理解汇总

unetr_plus_plus&#xff08;UNETR、nnU-Net&#xff09;系列数据处理理解汇总&#xff0c;这是一个 3D 图像分割的任务系列集。 为什么说他们是一个系列集合呢&#xff1f;主要是因为&#xff1a; 论文的训练和评价数据集是一样的&#xff0c;都是来自于10全挑战赛&#xff…...

稻盛和夫《活法》读后感

最近几天又重读了一边稻盛和夫的《活法》&#xff0c;里面的观点让我感触颇多&#xff0c;现分享给诸君。 稻盛和夫毕业后&#xff0c;适逢经济萧条&#xff0c;没有好机会进入大公司深造&#xff0c;只能在一名教授的推荐下进入了一家做陶瓷绝缘体的公司&#xff0c;虽然公司…...

Smurf 攻击是不是真的那么难以防护

Smurf攻击是一种网络攻击方式&#xff0c;属于分布式拒绝服务&#xff08;DDoS&#xff09;攻击的变种。以 1990 年代流行的名为 Smurf 的漏洞利用工具命名。该工具创建的 ICMP 数据包很小&#xff0c;但可以击落大目标。 它利用ICMP协议中的回声请求&#xff08;ping&#x…...

ASP.NET之图像控件

在ASP.NET中&#xff0c;用于显示图像的控件主要是Image控件&#xff0c;Image控件属于ASP.NET Web Forms的一部分&#xff0c;它允许你在Web页面上显示图像。以下是如何在ASP.NET Web Forms中使用 1. 添加Image控件到页面 在ASP.NET Web Forms页面上&#xff0c;你可以通过设…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...