离多态更近一步

在面向对象的语言里面,封装,继承,多态可谓是在熟悉不过了,当我们每次再去重新认识它们的时候总会有新的发现,为此我也经常感到疑惑,所以在这里和大家一起探讨三个问题,让我们在向多态靠近一点点。

虚表是否真的存在静态区

经常我们都会看见一个问题,虚表到底是存放在哪里的。当我们去往上查阅的时候都能出奇的发现一个答案,虚表是存放在静态区中的。对此我曾感到疑惑,存放数据的地方无非有栈,堆,静态区,常量区,首先我们可以排除堆区,因为系统不会自己去开一块空间来使用。接下来我们先了解一下虚表的存放体制,当一个对象在局部域中被创建出来并且它其中也有一张虚表,当局部对象被销毁后在另一个域类定义同一个对象,会发现它们使用的是同一张虚表。

 那么现在还剩下栈区,常量区,静态区,我们可以想一下如果虚表是存放在main函数里面的,那么也是符合条件的,同样,常量区和静态区更不用说。对此我们知道一个观念,相同的数据往往它们存放的地址不会太远,那么我们可以利用这一特征来检验一下看虚表离谁的地址比较近。

  通过这样一段代码我们可以清楚的看到,虚表的地址似乎离静态区相隔很远,但是离常量区似乎很近,那么我们是不是能得出,其实虚表是存放在常量区而并非静态区呢。

派生类中没被重写的虚表去哪里了

首先我们看这样一段代码

 基类中有一个fun1和fun2的虚函数,而派生类有两个不是重写的虚函数

 当我们想去虚表里面找到这个fun3和fun4的时候发现这两个函数并没有在虚表里面,那么问题来了,它们去哪里了呢。我们对此进行更深一步的研究,通过这个虚表去内存里面看看是否能找到消失的它。

 通过对比我们似乎还真找到了这两个地址,但是怎么证明这两个地址就是我们要找的小时的那两个函数呢。

对此我们在回过头来先在了解一下什么是虚函数表。简称虚表,它是由一个指针指向一块空间,空间里面存放了一些地址相连的值,并且这个地址是一个指向函数的指针。那么我们可不可以以认为虚表实际上是一个函数指针数组呢。

 既然这样我们就可以用一个函数指针来取出虚表里面的指针,然后通过这个指针去找到那个函数,对此可以先在函数里面添加一点用于标识的东西。

 我们用这样一段代码来证明我们的猜想。

 通过验证确实得出,那两个地址就是我们消失的fun3和fun4,这就说明,其实并不是不是重写的函数消失了,而是编译器通过特别的方式把它隐藏了起来。

多继承中被重写的虚函数会被放在哪个基类

假设由这样一段代码,一个子类继承成两个父类,那么在子类中,没有被重写的那个虚函数会被放在父类里面呢。

 最好的办法就是去实践它,我们去运行这段代码就会发现,实际上没有被重写的虚函数会被放在base1类里面

 但是我们似乎发现了一个不一样的点,同样是派生类对象D去调用的fun1,为什么调用的地址是不一样的呢。

我们先设置两个基类指针去指向派生类的对象, 然后通过反汇编去看一下具体是什么原因。先通过监视窗口去观察base1的虚函数表是个怎么样的

 可以看到,base1里面第一个第一个虚函地址为0x003f103c,然后主要看反汇编去调用这个函数的那句指令

 看到的是call了这个eax,那么我们再去看一下这个eax是一个什么

 可以看到eax和虚函数表里面第一个地址是一样的,就说明是找到了第一个函数,接下来就要开始调用了,我们按F11(逐语句调试)继续往下一步走

通过这个地址我们找到了fun1,继续往后走

 通过jmp我们拿到了fun1的真正的地址,最后我们得出结论,base1中的调用是正常的,接下来我们继续看看base2的调用

 通过base2同样来到了call这个地址

 eax同样和第一个虚函数同样的,到目前一切为止一切正常,我们继续往下走

同样的是一句jmp,继续往下走 

 来到这里我们就发现这里和上面不一样,当继续jmp的时候它并没有去到fun1的真正地址,而是执行了sub对ecx(*this指针)减去4,实际上是对*this指针减去了4。我们继续往下走

 当我们来到这里就会发现,这个地址好像就是在对base1进行fun1调用的时候出现的地址

 来到这一步我们才发现,最终base2也是调到了fun1但是它饶了一大圈。那么问题又来了,为什么base1不需要绕一大圈呢,这里咱们可以画个图理解一下

首先我们要知道我们要调的是derive的fun1,那么要调derive的fun1就要让指针指向derive对象,base1不用进行操作时因为derive恰好指向derive的开始,而base2却不是,所以要对它进行-4让它指向derive之后才真正开始调用fun1。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cisco学习笔记(CCNA)——Equipment Infrastructure Management

Equipment infrastructure management 路由器组件 路由器的组成及功能 CPU:执行操作系统的指令 随机访问存储器(RAM内存):RAM中内容断电丢失 只读存储器(ROM):开机自检软件,路由…

Jmeter 压测实战:Jmeter 二次开发之自定义函数

目录 1 前言 2 开发准备 3 自定义函数核心实现 3.1 新建项目 3.2 继承实现 AbstractFunction 类 3.3 最终项目结构 4 Jmeter 加载扩展包 4.1 maven 构建配置 4.2 项目打包 4.3 Jmeter 加载扩展包 5 自定义函数调用调试 5.1 打开 Jmeter 函数助手,选择自…

vue+Element项目中v-for循环+表单验证

如果在Form 表单里有通过v-for动态生成&#xff0c;如何设置验证呢&#xff1f; <el-form ref"ruleFormRef" :model"ruleForm" status-icon :rules"rules" label-width"120px"class"demo-ruleForm" hide-required-aster…

Zabbix-server监控mysql及httpd服务

目录 一、Zabbix监控mysql数据库 1、为server.Zabbix.com添加服务模板 2、创建mysql服务图形 二、server.zabbix.com服务器操作 编辑chk_mysql.sh脚本 三、server.Zabbix.com测试 四、查看web效果 五、Zabbix监控apache&#xff08;httpd服务&#xff09; 安装master 六、…

改进的北方苍鹰算法优化BP神经网络---回归+分类两种案例

今天采用前作者自行改进的一个算法---融合正余弦和折射反向学习的北方苍鹰(SCNGO)优化算法优化BP神经网络。 文章一次性讲解两种案例&#xff0c;回归与分类。回归案例中&#xff0c;作者选用了一个经典的股票数据。分类案例中&#xff0c;选用的是公用的UCI数据集。 BP神经网络…

Cesium态势标绘专题-多边形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

Redis追本溯源(三)内核:线程模型、网络IO模型、过期策略与淘汰机制、持久化

文章目录 一、Redis线程模型演化1.Redis4.0之前2.Redis4.0之后单线程、多线程对比3.redis 6.0之后 二、Redis的网络IO模型1.基于事件驱动的Reactor模型2.什么是事件驱动&#xff0c;事件驱动的Reactor模型和Java中的AIO有什么区别3.异步非阻塞底层实现原理 三、Redis过期策略1.…

【数字信号处理】带通采样定理及其MATLAB仿真

目录 一、带通采样定理1.1 内容1.2 公式推导 二、MATLAB信号仿真2.1 信号仿真实验2.2 MATLAB代码 三、总结参考 一、带通采样定理 按照奈奎斯特采样定理(低通采样)&#xff0c;采样频率 f s f_{s} fs​ 要大于等于信号中最高频率 f m a x f_{max} fmax​ 的2倍&#xff0c;才…

28.1 kibana

Kibana 是一个免费且开放的用户界面&#xff0c;能够对 Elasticsearch 数据进行可视化操作&#xff0c;从跟踪查询负载&#xff0c;到理解请求如何流经整个应用&#xff0c;都能轻松完成。 1.Kibana安装 注意要与ES版本保持一致 https://www.elastic.co/downloads/past-relea…

C# 目标平台为x64,自定义控件不可用,显示控件未能加载,错误解决方法

由于项目加载第三方的dll需要编译成x64&#xff0c;设置编译目标为x64 结果打开窗口设计器时&#xff0c;自定义的控件不能显示及加载 错误消息&#xff1a;未能找到类型“XXX”。请确保已引用包含此类型的程序集。如果此类型为开发项目的一部分&#xff0c;请确保已使用针对当…

Android 在程序运行时申请权限——以自动拨打电话为例

Android 6.0及以上系统在使用危险权限时必须进行运行时权限处理。 main_activity.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://sche…

HDFS的文件块大小(重点)

HDFS 中的文件在物理上是分块存储 &#xff08;Block &#xff09; &#xff0c; 块的大小可以通过配置参数( dfs.blocksize&#xff09;来规定&#xff0c;默认大小在Hadoop2.x/3.x版本中是128M&#xff0c;1.x版本中是64M。 如果一个文件文件小于128M&#xff0c;该文件会占…

【C#】并行编程实战:使用 Visual Studio 调试任务

并行编程可以提高应用程序的性能&#xff0c;但是调试起来会更困难&#xff0c;这一点在之前的章节中我们已经有了很直观的感受。对于程序而言&#xff0c;保证程序的正确性和保证性能同样重要。 本章将介绍可以在 Visual Studio 中的调试工具&#xff08;包括 Thread 窗口、Ta…

Qt Core学习日记——第三天QMetaEnum(上)

QMetaEnum用来代表枚举信息,内部也是访问moc文件。从moc文件中得到对应值 需要在头文件中声明 Q_ENUM,如下红框部分 moc中qt_meta_stringdata_XTest变为&#xff1a; qt_meta_data_XTest变为 static const uint qt_meta_data_XTest[] { // content: 8, // revision 0, // …

(二)RabbitMQ【安装Erlang、安装RabbitMQ 、账户管理、管控台、Docker安装 】

Lison <dreamlison163.com>, v1.0.0, 2023.06.22 RabbitMQ【安装Erlang、安装RabbitMQ 、账户管理、管控台、Docker安装 】 文章目录 RabbitMQ【安装Erlang、安装RabbitMQ 、账户管理、管控台、Docker安装 】**安装Erlang**安装RabbitMQ账户管理管控台Docker安装RabbitM…

打家劫舍系列

class Solution { public:int dp[105];//dp[i]表示偷取前i个房间获取的最大值int rob(vector<int>& nums) {// // dp[i][0];不偷取第i间房&#xff0c;偷取前i-1间房的最大值// //dp[i][1];偷取第i间房&#xff0c;偷取前i间房的最大值// memset(dp,0,siz…

案例研究|DataEase助力亚加达智能医学实验室场景BI展示

深圳市亚加达信息技术有限公司&#xff08;以下简称为亚加达&#xff09;成立于2018年&#xff0c;是一家专注于医疗信息系统研发的高科技公司&#xff0c;隶属于亚辉龙集团。 亚加达深入理解医疗实验室业务和日常工作流程&#xff0c;通过物联网和大数据技术&#xff0c;基于…

ubuntu环境安装centos7虚拟机网络主机不可达,ping不通

【NAT模式下解决】1.首先vi /etc/sysconfig/network-scripts/ifcfg-ens33检查ONBOOTyes&#xff0c;保存 2.输入systemctl restart network命令重启网关

GBASE南大通用出席CCF第38届中国计算机应用大会

在数据要素市场化分论坛上&#xff0c;GBASE南大通用高级副总裁赵伟发表“以自主可控的国产基础软件新兴技术保障数据要素安全高效流通”的主题演讲&#xff0c;向参会嘉宾分享基于GBASE数据库的自主可控国产软件&#xff0c;保障数据要素安全流通、高效流转的创新实践。 赵伟讲…

Maven学习笔记

Maven学习笔记 一、MAVEN基础1.1、Maven作用1.2、Maven基础概念1.2.1、仓库1.2.2、坐标1.2.2、仓库配置 1.3、 手动写一个maven程序1.4、依赖管理1.5、生命周期与插件1.5.1、构建生命周期1.5.2、插件 一、MAVEN基础 1.1、Maven作用 Maven的本质是一个项目管理工具&#xff0c…