【C++笔记】C++多态

【C++笔记】C++多态

  • 一、多态的概念及实现
    • 1.1、什么是多态
    • 1.2、实现多态的条件
    • 1.3、实现继承与接口继承
    • 1.4、多态中的析构函数
    • 1.5、抽象类
  • 二、多态的实现原理

一、多态的概念及实现

1.1、什么是多态

多态的概念:

在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。
计算机程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作。
多态也可定义为“一种将不同的特殊行为和单个泛化记号相关联的能力”。
多态可分为变量多态与函数多态。变量多态是指:基类型的变量(对于C++是引用或指针)可以被赋值基类型对象,也可以被赋值派生类型的对象。函数多态是指,相同的函数调用界面(函数名与实参表),传送给一个对象变量,可以有不同的行为,这视该对象变量所指向的对象类型而定。因此,变量多态是函数多态的基础。

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态 。
举个例子:比如 买票这个行为 ,当 普通人 买票时,是全价买票; 学生 买票时,是半价买票; 军人
买票时是优先买票。

1.2、实现多态的条件

实现多态的两个条件:

1、被调用的函数必须是虚函数,子类对父类的虚函数进行重写 (重写:三同(函数名/参数/返回值)+虚函数)
2、父类指针或者引用去调用虚函数。

举个例子:
在这里插入图片描述
这时候就实现了多态,即指向子类对象就调用子类对象的函数,如果指向的是父类对象,就调用的是父类对象的函数:
在这里插入图片描述
其实C++这里还有一个特殊情况,就是如果父类的同名函数加上了virtual修饰了,那么子类的同名函数就算不加virtual也是虚函数了,即也构成多态:
在这里插入图片描述
但我个人感觉函数加上的好,因为可能会形成误导。

强调:一定要是父类的指针或引用调用,如果是对象就变成了普通调用了:
在这里插入图片描述
此外虚函数的重写也还要满足三同:函数名、参数、返回值相同,只要有其中一个不满足也会变成普通调用。

但是这里还有非常尴尬的例外:“协变”,含义是虚函数的返回值类型可以不同,但又一个条件:子类和父类的返回值类型也必须是父子关系指针和引用。
在这里插入图片描述
其实“协变”也是C++常常被诟病的一点,因为它的应用场景实在太局限了,我也是感觉它有点儿多余了,我们只需要了解一下即可。

1.3、实现继承与接口继承

普通函数的继承实际上是一种实现继承,也就是继承了函数的逻辑:
例如:
在这里插入图片描述
这里继承的是函数的实现,所以变量_a改变了,输出的结果也就改变了。
而虚函数继承的是函数的接口,也就是父类和子类的接口是一样的,只是实现的逻辑不一样。其目的主要是为了重写,达成多态。
例如:
在这里插入图片描述
因为这里继承的只是接口,而实现逻辑是不同的,所以打印出来的内容也就不同。也就实现了多态。
之所以说是子类继承了父类的接口,是因为如果我们改变子类中的虚函数的默认参数是不起作用的:
在这里插入图片描述
所以这也就解释了为什么子类的虚函数没有加virtual也依然是虚函数,因为其接口就是继承了父类的。

1.4、多态中的析构函数

我们先来看一个现象:
在这里插入图片描述
相信大家都能看出这段代码的问题,这很明显值是一个普通调用。但是它new了一个B对象却只调用了A类的析构函数,这岂不是有内存泄漏的风险?
那该怎么解决这个问题呢?如果要将析构函数也实现成多态的调用的话,那子类和父类的析构函数名不可能相同啊,不是冲突了?

C++正是为了解决这个问题,对构造函数进行了一些处理:
因为多态的原因,编译器在底层会将析构函数的函数名统一处理成destructor()。
所以我们表面上看到的析构函数是是不同名的,实际在底层他们都叫destructor(),所以也就能实现多态了:
在这里插入图片描述

1.5、抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。
抽象类还有以下三个注意点:

1、子类继承抽象类后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。
2、父类的纯虚函数强制了派生类必须重写,才能实例化出对象。
3、纯虚函数也可以写实现{ },但没有意义,因为是接口继承,而子类被强制了重写纯虚函数,所以{ }中的实现会被重写;父类没有对象,不能调用父类的实现,所以父类实现纯虚函数也就没有意义了。

其实各种抽象的事物都可以定义成抽象类,比如人、动物、汽车、水果……,也就是它不具体指哪一个事物,只是抽象的代表默写事物的总体特征。

比如说动物:
在这里插入图片描述

二、多态的实现原理

这里有一个类,我们试试来求一下它的大小:
在这里插入图片描述
首先要说一点,不管是普通成员函数还是虚函数都是不储存在类里面的,都是存在代码段的。
可这里的类的大小为什么是8字节呢?不应该是4字节吗?
说明类里面一定还存了别的什么东西,我们可以到监视窗口看看:
在这里插入图片描述
我们会发现除了成员_a之外还多了一个_vfptr的东西,这个其实是一个虚表指针,它的本质是一个数组指针,指向一个函数指针数组,而被指向的这个函数指针数组就是虚表。
由于平台的不同,虚表的位置也有可能不同,有的实在类的最前面有的可能是在类的最后面。
一个含有虚函数的类至少有一个虚表指针。
我们可以到内存中去仔细的看看A类的结构:
在这里插入图片描述
然后我们可以来看看虚表中到底有什么:
在这里插入图片描述
所以我们可以来打印一下虚表中的内容,看看它们是不是函数的地址,如果是的话试试调用它:
在这里插入图片描述
从结果来看确实是函数的地址,因为所有的虚函数的地址都会存进虚表,所以这里会打印四个。

有了上面的铺垫我们就可以来解释多态的真正原理了。
我们先让一个B类继承A类,并重写func函数:
在这里插入图片描述
然后我们再取出A类和B类的虚表对比看看:
在这里插入图片描述
我们发现两个对象中的虚表里,只有被重写的func()函数的地址不同,而没有重写的print1()的地址则相同。所以虚函数的重写也被称为是虚函数的覆盖(其实是虚函数表的覆盖)。

有了以上的铺垫,在我提出以下结论的时候,才会逻辑自掐:
多态的实现机制其实就是,傻傻地通过虚表指针找到虚表,再找到对应的虚函数。

之所以这种“傻傻”的行为能成功,是因为在父类指针或引用指向子类的时候会发生“切片”:
在这里插入图片描述
A类的指针只会指向B对象中A类部分的内容,所以也就只会在A类部分的虚表中查找。就算B类有多张虚表(当B类有多个直接父类时候就会有多张虚表)。父类A的指针通过切片之后也只会指向A类部分。
且因为,虚表的位置在某个类中都是固定的,所以偏移量也都是固定的,所以B类有多少个直接父类,他们父类的指针的寻址操作也都是统一的。

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

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

相关文章

通讯网关软件031——利用CommGate X2HTTP实现HTTP访问ODBC数据源

本文介绍利用CommGate X2HTTP实现HTTP访问ODBC数据源。CommGate X2HTTP是宁波科安网信开发的网关软件,软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示,实现上位机通过HTTP来获取ODBC数据源的数据。 【解决方案】设置网关机…

【设计模式】第12节:结构型模式之“外观模式”

一、简介 门面模式,也叫外观模式,英文全称是Facade Design Pattern。门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。 目的:简化复杂系统的交互方式 特点:提供一个统一的交互接口 二、UML类…

有一个 3*4 的矩阵,找出其中值最大的元素,及其行列号

1解题思路&#xff1a; 首先学会输入二维数组&#xff1b;然后知道如何比较求最大值&#xff1b;最后就是格式问题&#xff1b; 2代码&#xff1a; #include<stdio.h> int main() {int a[3][4];int i,j,max,row,line;for(i0;i<3;i){printf("请输入二维数组\n&…

蓝桥杯动态规划-第五弹 最大子数组和 买卖股票最佳时机IV 第N个泰波那契数 环形数组

目录 一、最大子数组和 二、买卖股票最佳时机IV 三、第N个泰波那契数 四、环形数组 一、最大子数组和 1.状态表示 dp[i]:到第i数字&#xff0c;所有的最大和。 2.状态转移方程 dp[i]max(dp[i-1]p[i]&#xff0c;p[i])(加入这个点是0&#xff09; 我们来想一下&#xff0c;这…

【Spring】Spring MVC请求响应

文章目录 1. 请求1.1 传递单个参数1.2 传递多个参数1.3 传递对象1.4 后端参数重命名1.5 传递数组1.6 传递集合1.7 传递JSON对象1.8 获取URL中参数1.9 上传⽂件1.10 获得Cookie1.11 获得Session1.12 获得Header 2. 响应2.1 返回静态界面2.2 返回数据2.3 返回HTML代码片段2.4 返回…

Vue3 + Tsx 集成 ace-editor编辑器

Ace Editor介绍 Ace Editor&#xff08;全名&#xff1a;Ajax.org Cloud9 Editor&#xff09;是一个开源的代码编辑器&#xff0c;旨在提供强大的代码编辑功能&#xff0c;通常用于构建基于Web的代码编辑应用程序。它最初由Cloud9 IDE开发&#xff0c;现在由开源社区维护。 主…

SAM:Segment Anything 代码复现和测试 基本使用

相关地址 代码&#xff1a; https://github.com/facebookresearch/segment-anything 在线网站&#xff1a; https://segment-anything.com/demo 环境配置 建议可以clone下来学习相关代码&#xff0c;安装可以不依赖与这个库 git clone https://github.com/facebookresearch…

前端HTML

文章目录 一、什么是前端前端后端 前端三剑客1.什么是HTML2.编写前端的步骤1.编写服务端2.浏览器充当客户端访问服务端​ 3.浏览器无法正常展示服务端内容(因为服务端的数据没有遵循标准)4.HTTP协议>>>:最主要的内容就是规定了浏览器与服务端之间数据交互的格式 3. 前…

Angular-03:组件模板

各种学习后的知识点整理归纳&#xff0c;非原创&#xff01; 组件模板 ① 数据绑定② 属性绑定③ 类名绑定④ 样式绑定⑤ 事件绑定⑥ 获取原生DOM对象6.1 在组件模板中获取6.2 在组件类中获取 ⑦ 双向数据绑定⑧ 内容投影8.1 select选择器8.2 单槽投影8.3 多槽投影 ⑨ 安全操作…

【Overload游戏引擎细节分析】PBR材质Shader---完结篇

PBR基于物理的渲染可以实现更加真实的效果&#xff0c;其Shader值得分析一下。但PBR需要较多的基础知识&#xff0c;不适合不会OpenGL的朋友。 一、PBR理论 PBR指基于物理的渲染&#xff0c;其理论较多&#xff0c;需要的基础知识也较多&#xff0c;我在这就不再写一遍了&…

leetcode:374. 猜数字大小(二分查找)

一、题目 函数原型&#xff1a;int guessNumber(int n) 二、思路 本题其实就是从 1 - n 中找出所要的答案。利用guess函数来判断数字是否符合答案。 答案小于当前数字&#xff0c;guess函数返回-1 答案等于当前数字&#xff0c;guess函数返回0 答案大于当前数字&#xff0c;gue…

nginx 转发数据流文件

1.问题描述 后端服务&#xff0c;从数据库中查询日志&#xff0c;并生成表格文件返回静态文件。当数据量几兆时&#xff0c;返回正常&#xff0c;但是超过几十兆&#xff0c;几百兆&#xff0c;就会超过网关的连接超时时间30秒。 时序图 这里面主要花费时间的地方在&#xff…

启动Vue项目报错Error: error:0308010C:digital envelope routines::unsupported

问题描述 启动Vue项目报错Error: error:0308010C:digital envelope routines::unsupported 出现这个一般就是node版本的问题&#xff0c;通过命令查看node -v查看node版本&#xff1b; 百度查了好多&#xff0c;都让我降低node版本&#xff0c;属实太麻烦了 在不改node版本的…

【C# Programming】委托和lambda表达式、事件

目录 一、委托和lambda表达式 1.1 委托概述 1.2 委托类型的声明 1.3 委托的实例化 1.4 委托的内部机制 1.5 Lambda 表达式 1.6 语句lambda 1.7 表达式lambda 1.8 Lambda表达式 1.9 通用的委托 1.10 委托没有结构相等性 1.11 Lambda表达式和匿名方法的内部机制 1.1…

博弈论学习笔记(2)——完全信息静态博弈

前言 这部分我们学习的是完全信息静态博弈&#xff0c;主要内容包括博弈论的基本概念、战略式博弈、Nash均衡、Nash均衡解的特性、以及Nash均衡的应用。 零、绪论 1、什么是博弈论 1&#xff09;博弈的定义 博弈论&#xff1a;研究决策主体的行为发生直接相互作用时候的决策…

Java架构师软件架构的演化和维护

目录 1 导学2 软件架构演化和定义3 面向对象软件架构演化4 软件架构演化方式的分类5 软件架构演化原则6 软件架构演化评估方法7 大型网站架构演化8 软件架构维护想学习架构师构建流程请跳转:Java架构师系统架构设计 1 导学 2 软件架构演化和定义 软件架构的演化和维护就是对…

Kafka - 异步/同步发送API

文章目录 异步发送普通异步发送异步发送流程Code 带回调函数的异步发送带回调函数的异步发送流程Code 同步发送API 异步发送 普通异步发送 需求&#xff1a;创建Kafka生产者&#xff0c;采用异步的方式发送到Kafka broker 异步发送流程 Code <!-- https://mvnrepository…

飞鼠异地组网工具全网互通实战指南

飞鼠异地组网工具全网互通实战指南 一、飞鼠异地组网工具介绍1.1 飞鼠工具简介1.2 飞鼠工具官网 二、本次实践介绍2.1 本次实践前提2.2 本次实践简介2.3 本次实践环境规划 三、异地组网配置3.1 进入中心控制器节点管理后台3.2 网卡设置3.3 进入子网节点管理后台3.4 网卡设置 四…

项目综合实训,vrrp+bfd,以及策略路由的应用

目录 一&#xff0e; 项目需求 二&#xff0e; Visio设备画图 三&#xff0e; 设备选型 三&#xff0e;vlan规划 四&#xff0e;Ip地址规划 五&#xff0e;实验拓扑图 六&#xff0e;配置过程及结果 项目需求 1.S1作为VLAN10的主网关和根桥&#xff0c;S2作为v…

Pytorch L1,L2正则化

L1正则化和L2正则化是常用的正则化技术&#xff0c;用于在机器学习模型中控制过拟合。它们的主要区别在于正则化项的形式和对模型参数的影响。 L1正则化&#xff08;Lasso正则化&#xff09;&#xff1a; 正则化项形式&#xff1a;L1正则化使用模型参数的绝对值之和作为正则化…