多态的概念

多态

    所谓的多态其实就是多种形态,它又被分为编译时多态(静态多态) 和 运行时多态(动态多态)。

静态的多态其实就是之前的模版和函数重载,今天我们主要讲动态的多态。所谓的动态多态其实就是相同的函数,完成不同的功能。

 

这就实现了明明都是用A&类型的参数,但是调用的结果不一样的情况出现。要达成这一点其实有两个地方需要注意,首先,必须是父类的指针或者引用,不然切片切不好。其次,需要父类和子类函数的返回类型、函数名、参数类型都要一样,这样,在前面加上一个virtual就可以达成虚函数的重写了。

当然这里有两个需要注意的地方:子类可以不加virtual,但是父类一定要加。另一个则是协变,这个比较少出现,它允许返回类型可以不一样,不过必须是派生类的引用或指针,也许是考虑到可能派生类里面就包含有父类的关系。

一旦上面的两个条件不满足,那就根据传入的是A,两次都调用666。所以在写多态之前先检查一下自己的条件满不满足。

 

同时它也不是非得局限于一个已有函数的另一种方法,它可以只是一个笼统的对象。

 这里有一个老六题,做完这个,想必理解会大大提升。

根据我们之前讲的,毋庸置疑这是一个虚函数的重写,之后创建了一个B类型的指针,然后再调用了test,这里是第一个坑,大伙可能觉得,这B指针不满足两个条件啊,但实际上,因为继承的关系调用test其实是把B切片了传入到A里,所以test的this是A*,满足了条件,第二个坑来了,按照我们说的,各自实现各自的,那么这里应该会打印B->0。 但是实际上打印的是B->1.

为什么呢,因为这个是重写,我用的参数并不是用B的 我用的是A的,只有内容我才按B的实现。

所以我用A的缺省参数来实现B的打印,自然就变成了B->1这么个奇怪的答案。

所以咱以后注意虚函数的重写部分就好了,派生类的缺省值就别参考了。

析构函数其实也有坑,或者说隐藏点,那就是析构函数其实都会被处理为为destructor,所以virtual加析构函数也是达成重写的,那么为什么要这么做呢。

其实就是为了应对这种情况的,站在我们的角度当然知道p1调用A的析构 p2调用B的析构,但是编译器不知道,它只会根据类型调对应的析构,那这就出现内存泄漏的问题了,所以祖师爷采取了这种方法来解决问题,所以,在父子类之中有用到new的,顺便把析构给实现重写。 也因为会被处理为destructor所以如果即使没有new,那父类和子类的析构也是构成隐藏关系的。同时子类调用构造是先调用父类再调用子类,那么到了析构,就是反过来先调用子类,再调用父类。而子类必定是包含有父类的内容的,所以子类的析构还会额外调用一次父类的析构。

到这里想必一定有粗心的兄弟,可能不会时时刻刻关注虚函数是否满足两个必备条件,所以祖师爷给了我们一个关键字 override 它可以判断你是否构成重写,如果是,那就无事发生,不然就给你报错,可以理解为虚函数专属的assert 不过它不会温和的提示你出问题,而是直接无法运行。

它会很明确的告诉你没重写。

而如果我们不想有别人重写我们的父类,那我们可以用到另一个关键字final。可以翻译理解为最终类,无法被继承和重写。 放类名后就是最终类,不能被继承,放函数后面就是不能被重写。

不过这俩是后面更新出来的,也许有一些非常老的版本是不认的。

这些概念其实如果理解了,是不用专门记的。

抽象类

其实就是之前我们不写函数实现部分,只给参数返回类型。

这玩意其实就是只给你参数,但是不给你实现,所以它不能被实例化。但是父类的指针或者引用是可以的。实现交给子类来写,子类不写光继承也不能实例化出对象来。这样也代表了它没有父类对象。完全根据子类写的实现运行。

有点类似于模版。

虚表

咱笼统的理解起来就是 带virtual的它内部都有一个隐藏的指针,所以计算大小要多算一个指针进去,这个指针准确的说是函数指针数组,它是虚函数用来存储被重写的函数的地址的。

这个vfptr其实就是虚函数的指针,它的大小取决于你这有多少个虚函数。

我们创建多少个对象,就有多少个虚表。这个过程呢就是如果你满足多态,那么我不会和正常函数一样直接调用,而是去你的虚表里面找你的地址,调用的是地址里的函数,私人订制实现。

而父类子类之间,虚表表面上其实是一个虚表,至少刚切片完是这样的,但是如果你重写了实现,那么子类的虚表就会更新覆盖之前的函数地址,所以此时虽然它表面还是父类的虚表,但是实际上它的内容已经是子类重写后的了。这也是虚函数能实现重写的本质。

知道了这一点,是不是就感觉它没那么高级了,遇到虚函数的动作都是一样的,只是它里面的地址更不更新的区别,更新了就不一样,没更新就一样。

可以看到,虚函数它里边多了很多指针跳转的步骤,一层一层找进去,然后才call的地址。

而普通的函数就很短了,我才不管你是谁,我直接根据你的地址找进去然后call就完了。

这上面也是静态绑定和动态绑定的区别,静态就是普通函数调用,虽然也能调用,但是显得有点呆板,而动态就是虚函数的调用,它就很智能,至少看起来很智能。不过动态这么多行指令效率就会比静态低一点。

小小总结一下:

虚表不同只是不同类用不同的虚表,同类其实是共享一个虚表的,因为咱的函数实现是一样的,那何必浪费内存区创建多个冗余对象呢。

而子类由两个部分组成,我们之前提到的继承下来的父类虚表和自己的虚表。表面上父类还是父类,实际上里面存的是重写后的地址。

子类的虚表又由三个部分组成:父类的虚函数地址,子类的虚函数地址,重写后的虚函数地址。

所以虚函数的本质其实是一个存虚函数指针的指针数组,而这个数组后面其实放了一个0x00000000的标记,类似于\0的作用,不过这个不是C++规定的是各个编译器定义的,vs系列会放,g++就没放。

那么虚函数表存在哪里呢,堆区?有点不太合理,因为我们并没有看到它的释放。栈?出了作用域销毁显得更不合理了,经过对比一下发现这玩意存在常量区里,不过这是vs的版本,实际上c++没规定放哪,所以可能会因为编译器的问题有所差异。

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

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

相关文章

《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》P84

更正卷积与相关微课中互相关运算动画中的索引。 1-D correlation rectwave 禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》 禹晶、肖创柏、廖庆敏《数字图像处理》资源二维码

【Linux实践】实验一:Linux系统安装与启动

【Linux实践】实验一:Linux系统安装与启动 实验目的实验内容实验步骤及结果1. 下载VMware2. 下载 Linux 操作系统3. 在VMware中安装Ubuntu系统4. 配置Ubuntu系统5. 关机 实验目的 1.掌握Linux系统的安装过程和简单配置方法。 2.掌握与Linux相关的多操作系统的安装方…

【Leetcode算法面试题】-1. 两数之和

文章目录 算法练习题目思路参考答案算法1算法2算法3 算法练习 面试经常会遇到算法题目,今天开启算法专栏,常用算法解析 题目 ** 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数&…

为拖延症量身定制的AI工具,让Kimi做我的《每日信息整理助手》

AI不止对传统行业带来巨大的改变,对日常生活也便利了不少,现在这个时代获取信息的方式太简单了。 我们每天都会接受大量的信息,难免一天下来会忘记很多事情,有时候突然想起了一个点子,有时候突然有一件急事、一件待办事…

17 个被动和主动遥感之间的区别

摘要: 遥感是指通过使用连接到卫星的传感器记录有关地球表面信息的行为。遥感在收集大面积信息、表征地球上的自然特征、观察和监测地球和物体随时间的变化以及 利用这些信息进行处理和分析方面发挥着至关重要的作用。在遥感中,太阳是终极能源,对照明非常有用。卫星具有成像传…

LCS—最长公共子序列

最长公共子序列问题就是求出两个字符串的LCS长度,是一道非常经典的面试题目,因为它的解法是典型的二维动态规划。 比如输入 str1 "babcde", str2 "acbe",算法应该输出3,因为 str1 和 str2 的最长公共子序列…

金属铬厂商分析:前十强厂商占有大约64.0%的市场份额

金属铬是一种灰色、有光泽、硬而脆的过渡金属。铬是不锈钢的主要添加剂,可增加耐腐蚀性。 据QYResearch调研团队最新报告“全球金属铬市场报告2024-2030”显示,预计2030年全球金属铬市场规模将达到11.8亿美元,未来几年年复合增长率CAGR为6.5%…

Spring Cloud之三 网关 Gateway

1:Intellij 新建项目 spring-cloud-gateway 2:pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLoca…

CnCrypt(磁盘加密工具绿色版是一款功能强大磁盘加密工具,供大家学习研究参考

CnCrypt(磁盘加密工具)特点 加密单个分区或整个硬盘,所有加密都是以分区为基础的 提供两级方案,以应对被强迫说出密码的情况(如抢劫。隐藏分区(覆盖式密码术,steganography)无法探测到CnCrypt 加密分区(加密数据会被认为是随机数据)。 CnCrypt(磁盘加密工具)特色 1、加密U…

【C++】C++ STL 探索:List使用与背后底层逻辑

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现 本文将通过模拟实现List&#xff0c;从多个角度深入…

vue-router + el-menu

1. el-menu的router属性 在el-menu中有一属性&#xff1a;router&#xff0c;默认是false 1.1 使用默认配置&#xff0c;即false 这时候需要自己在点击子菜单的时候进行导航&#xff0c;在el-menu添加方法&#xff0c;里边有三个参数 index: 选中菜单项的 index,indexPath…

本地部署大语言模型详细操作步骤

本地部署大语言模型&#xff0c;尤其是像我这样的基于中文开源项目的大模型&#xff0c;涉及的步骤相对复杂&#xff0c;需要一定的技术背景。下面我将详细阐述整个流程&#xff1a; 环境准备&#xff1a; 硬件准备&#xff1a;大语言模型通常需要高性能的计算资源&#xff0c;…

uni-app实现web-view和App之间的相互通信

双向实时 如果app端部署成网站&#xff0c;则web-view就是iframe&#xff0c;使用也可以双向通讯 https://uniapp.dcloud.net.cn/component/web-view.html APP端代码 index.vue: <template><web-viewid"m-webview":fullscreen"true":src"…

不同vlan之间的通信方法

1.通过路由器的物理接口 1.给PC1,PC2配置IP地址&#xff0c;网关2.进入交换机配置vlan&#xff0c;交换机所有口都配置access口并绑定vlan3.配置路由器&#xff0c;进入路由器的两个接口配置网关IP和掩码缺点&#xff1a;成本高&#xff0c;每增加一个vlan就需要一个物理端口和…

辛巴赔付到账,罗永浩退一赔三:直播带货终于往好方向卷了下…

因为快手顶流辛巴扔出的一颗重磅炸弹「被辛巴架火上烤&#xff0c;带货顶流圈快乱成一锅粥了……」&#xff0c;把直播带货行业藏在深处的淤泥炸出了水面。 原本表面看上去清澈、安静的水面&#xff0c;越来越浑&#xff0c;且还冒着火星子&#xff01;‍‍‍‍‍‍‍ 自从这个…

无人机电调接线

接线方式&#xff1a; 电调的作用是将飞控板的PWM控制信号转变为电流信号 因为电机的电流是很大的&#xff0c;通常每个点击正常工作时都平均有3A左右的电流&#xff0c;如果没有电调的存在&#xff0c;飞控无法承受这么大的电流。 电调的选择&#xff1a;电调上标的电流值是…

六、图结构

文章目录 一、引入二、基本概念三、图的表示四、图的遍历4.1 图的深度优先遍历&#xff08;DFS&#xff09;4.2 图的广度优先遍历&#xff08;BFS&#xff09;4.3 图的深度优先 VS 广度优先 一、引入 二、基本概念 三、图的表示 package com.gyh.grapg;import java.util.ArrayL…

OpenCV结构分析与形状描述符(22)计算图像中某个轮廓或区域的矩函数moments()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算一个多边形或光栅化形状直到三阶的所有矩。 该函数计算一个向量形状或光栅化形状直到三阶的矩。结果返回在 cv::Moments 结构中。 函数原型…

ATF UFS初始化笔记

1. JESD220 中关于UFS初始化的描述 原文 13.1.3 Initialization and boot code download process The initialization and boot code download process is made up of the following phases: partial initialization, boot transfer and initialization completion. 13.1.3.…

List<Map<String, Object>>汇总统计排序

开发环境&#xff1a;jdk 1.8 需求一&#xff1a; 1、统计每个小时(升序)不同事件的产品产量 2、统计不同事件&#xff08;OK 、NG&#xff09;的总产量 public static void main(String[] args) {//数据源List<Map<String, Object>> list new ArrayList<Map…