C++相关概念和易错语法(22)(final、纯虚函数、继承多态难点)
1.final
final在继承和多态中都可以使用,在继承中是指不想将自己被继承,在多态中是指不想该函数被重写,比较简单,下面是一些使用例子。
2.纯虚函数
当我们需要抽象一个类的时候,我们就需要用到纯虚函数。所谓抽象的类是指高度概括的,需要针对不同事物有不同处理的。如植物是一种抽象的类,而像苹果、香蕉就是具象的,单独讨论植物太过庞大,没有太大意义,因此我们的重心放在由植物具象出来的苹果,我们可以具体讨论它的成分、营养价值等。理解了这个例子,就能理解为什么有抽象类,纯虚函数的存在了。
这就是一个纯虚函数,就是在虚函数后面加上 = 0,它对应的类就叫抽象类。注意,只要有一个纯虚函数,这个类就叫抽象类,抽象类不能被实例化,就算你不打算用这个纯虚函数。唯一能做的就是调用这个类里面的static成员,因为它们不需要实例化就能调用
这么做的意义就在于纯虚函数对应的类本身就高度抽象,实例化它没有意义。但我们可以讨论将它具象化的事物,这就要用到虚函数的重写功能。我们可以理解,纯虚函数存在的意义是依赖于虚函数的性质存在的,这里需要我们深刻思考。
3.继承、多态难点
继承、多态的用法、意义几乎讲的差不多了,绝大多数情况下已经够用了,只不过在极少数情况下仍有一些坑。
(1)多态调用重写的函数
先看下面的代码,想想结果是什么
#include <iostream>
using namespace std;class A
{
public:virtual void test(int a = 0){}
};class B final : public A
{
public:void test(int a){cout << a << endl;}
};int main()
{A* a = new B;a->test();return 0;
}
不少人会想,这难道不报错吗?但结果是
我们需要知道,当构成多态和重写时,调用函数是以父类声明+子类定义进行的,对于三个类及以上都是如此,这个父类指的是构成多态的父类
我们也可以进一步理解为什么只需要父类写virtual,子类可以不写,因为子类的函数声明根本没有意义(在多态中),写不写都是以父类的声明为标准。但是在多态语法以外就不会出现这种反直觉处理情况了。
(2)继承调用父类函数时this的类型变化
先看看下面的代码,想想test2的隐含的this指针是B*还是A*
#include <iostream>
using namespace std;class A
{
public:virtual void test(int a = 0){} void test2(){test();}
};class B final : public A
{
public:void test(int a = 1){cout << a << endl;}
};int main()
{B* a = new B;a->test2();return 0;
}
既然是B*调用函数,那理所应当应该是B*为形参来接受啊,但实际不是这么理解的。
当子类去调用父类的成员函数时,隐含的指针类型始终是父类的。要理解这里,我们假设这个指针的类型是子类的,那如果子类又写了一个一模一样的函数构成隐藏,那么就会因为参数和假设的函数完全相同而报错,所以是行不通的。
当子类调用父类时,this指针会发生一次赋值兼容转换,这里是从B*赋值兼容转换为A*,赋值兼容转换为指针只会影响访问的方式,指针的值,指向的内容都不会改变。但学了多态之后,我们是否可以将这种特性和多态的形成条件结合起来呢?上面这段代码就是如此。
结合上一个易错点,这段代码的最终结果是
(3)多态访问限制的特殊处理
先看看下面的代码,看看是否能够正常访问
#include <iostream>
using namespace std;class A
{
public:virtual void Test(){cout << "A" << endl;}
};class B : public A
{
private:void Test(){cout << "B" << endl;}
};int main()
{A* p = new B;p->Test();return 0;
}
很多人以为p的类型是A*,A访问不了B,但其实程序运行没有问题
我们要理解访问限定符限制的是什么,是防止其它类调用private的函数,这里p是一个指针,本身就指向B对象的空间,只不过访问方式按A进行。由于符合多态的条件,就按虚函数表进行访问。那么问题在于:B会不会阻止呢?
我们先看看什么情况是会阻止的
我们发现无论在A还是在main函数中,都没有办法调用B中的private成员,这也符合我们之前的预期。但是为什么A* p = new B; p->Test();这种操作就可以呢?
事实上,这是多态中的特殊处理,当我们用父类的指针或引用来访问子类的虚函数时,是会以父类的访问限定符为标准的。子类的限制不会起到作用。同理,就算子时public,父是private,那么就无法访问。如何理解?
当以父类的指针和引用调用虚函数时,是按照虚函数表的地址直接去调用函数,根本不会经过类调用函数,只会受到调用方(父类)的访问限定符的限制。
一般建议都设为public
4.动静态绑定
动静态绑定都是为了定位一个函数,从反汇编的角度上讲就是确定call的对应的地址是什么,只不过两者的方式有一定的区别。
(1)动态绑定:多态调用函数的核心时动态绑定,也叫运行时绑定。也就是借助虚函数表,在这个函数指针数组中确定函数的地址。
(2)静态绑定:我们平时写的函数都可以认为是静态绑定(包括函数重载、普通函数、模板函数),函数如果声明定义在一起就在编译后进符号表,如果声明定义分离在两个文件则在链接时进符号表,运行时是根据符号表来查找函数。
在多态中,在满足动态绑定的情况下我们指定类域调用函数那就自动转为静态绑定,就失去了多态的特性。
#include <iostream>
using namespace std;class A
{
public:virtual void Test(){cout << "A" << endl;}
};class B final : public A
{
public:void Test(){cout << "B" << endl;}
};int main()
{A* p = new B;p->Test();p->A::Test();return 0;
}
运行结果
5.继承、多态的一些知识点和处理技巧
(1)多用const修饰函数,保证匿名对象传参可以调用函数
(2)函数第一句的指令理解为函数的地址,成员函数要打印它们的地址函数名前要加&,其余函数函数名就是它的地址(&可加可不加,但成员函数一定要加)
(3)cout打印地址很麻烦,char*不会打印地址,会按字符串去打印,这跟流插入的重载有关。有几个关于函数指针的重载会导致出现bug,打印地址很受阻,最好使用printf
(4)关联性强的类型之间支持隐式类型转换,如整型家族+double(内置类型)、指针之间,有的支持强转,如int和int*。
关联性弱的自定义类型,想取头地址,可以使用*((int*)&Base),虚函数表的地址就在类的开头
(5)只有virtual修饰的成员函数才能叫作虚函数,而像static修饰的成员函数、全局函数都不能定义为虚函数,全局定义的虚函数没有意义,static修饰的成员函数不属于对象,就算加了virtual,也进不了虚函数表,没有意义。
(6)virtual修饰的成员函数声明定义分离时定义处不写virtual
(7)友元不是成员函数,所以不能用virtual修饰
(8)多继承可能有多张虚函数表,按继承顺序排序,但单继承对应的就只有一张虚函数表,如果多继承后自己又写了虚函数,则默认放在第一张虚函数表后面
(9)如果不重写虚函数,那共用同一个函数,如果所有的函数都不重写,两个类存的函数的地址都相同,但是这对应两张虚函数表,开辟的是不同的空间
(10)虚函数表是在编译期间就形成了。而多态是动态绑定(运行时绑定/晚期绑定),是因为编译时编译器只负责检查语法错误,而不负责读取内容,只有运行起来才知道函数调用的地址。
相关文章:
C++相关概念和易错语法(22)(final、纯虚函数、继承多态难点)
1.final final在继承和多态中都可以使用,在继承中是指不想将自己被继承,在多态中是指不想该函数被重写,比较简单,下面是一些使用例子。 2.纯虚函数 当我们需要抽象一个类的时候,我们就需要用到纯虚函数。所谓抽象的类…...
状态管理的艺术:探索Flutter的Provider库
状态管理的艺术:探索Flutter的Provider库 前言 上一篇文章中,我们详细介绍了 Flutter 应用中的状态管理,以及 StatefulWidget 和 setState 的使用。 本篇我们继续介绍另一个实现状态管理的方式:Provider。 Provider优缺点 基…...
玩转HarmonyOS NEXT之IM应用首页布局
本文从目前流行的垂类市场中,选择即时通讯应用作为典型案例详细介绍HarmonyOS NEXT的各类布局在实际开发中的综合应用。即时通讯应用的核心功能为用户交互,主要包含对话聊天、通讯录,社交圈等交互功能。 应用首页 创建一个包含一列的栅格布…...
GPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建
原文链接:GPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608565&idx3&snd4e9d447efd82e8dd8192f7573886dab&chksmfa826912cdf5e00414e01626b52bab83a96199a6bf69cbbef7f7fe…...
记录些MySQL题集(4)
1、数据库的三范式是什么? 第一范式:列不可再分 第二范式:在第一范式的基础上,要求数据库表中的所有非主键列完全依赖于主键,而不是仅依赖于主键的一部分 第三范式:满足第二范式的基础上,所有…...
pdf提取其中一页怎么操作?提取PDF其中一页的方法
pdf提取其中一页怎么操作?需要从一个PDF文件中提取特定页码的操作通常是在处理文档时常见的需求。这种操作允许用户选择性地获取所需的信息,而不必操作整个文档。通过选择性提取页面,你可以更高效地管理和利用PDF文件的内容,无论是…...
godot使用ws
go服务端 package mainimport ("encoding/json""fmt""github.com/gorilla/websocket""net/http" )var upgrader websocket.Upgrader{ReadBufferSize: 1024,WriteBufferSize: 1024, }// 处理函数 func handleWebSocket(w http.Respo…...
Windows FFmpeg 开发环境搭建
FFmpeg 开发环境搭建 FFmpeg命令行环境搭建使用FFmpeg官方编译的库Windows编译FFmpeg1. 下载[msys2](https://www.msys2.org/#installation)2. 安装完成之后,将安装⽬录下的msys2_shell.cmd中注释掉的 rem set3. 修改pacman 镜像源并安装依赖4. 下载并编译源码 FFmpeg命令行环境…...
链路聚合概述
目录 技术背景: 基本概念: 链路聚合的工作模式: 简介: 1)Manual Load-balance(手动负载分担) 简介: 配置实施: 2)LACP(链路聚合控制协议ÿ…...
【链表】算法题(二) ----- 力扣/牛客
一、链表的回文结构 思路: 找到链表的中间节点,然后逆置链表的后半部分,再一一遍历链表的前半部分和后半部分,判断是是否为回文结构。 快慢指针找到链表的中间节点 slow指针指向的就是中间节点 逆置链表后半部分 逆置链表后半部分…...
合成复用原则
合成复用原则 (Composite Reuse Principle, CRP) 合成复用原则(Composite Reuse Principle, CRP),也被称为组合/聚合复用原则,是面向对象设计中的一条重要原则。它的核心思想是:优先使用对象组合/聚合,而不…...
安卓自带camera hal3 实例README.md翻译
最近,遇到一个这样的问题,临时了解下这个驱动实现架构和特点,翻译如下 V4L2相机HALv3 camera.v4l2库使用视频Linux 2(V4L2)接口实现了camera HAL v3。这使得它在理论上可以与各种设备配合使用,尽管V4L2的…...
ActiViz实战:ActiViz中的自己实现鼠标双击事件
文章目录 1、添加鼠标事件2、网上实现双击事件的方式3、增加双击的时间限制4、补充说明1、添加鼠标事件 已知在C#中观察者/命令模式会报错,正常添加鼠标事件如下: private void VtkInteractorStyleTest() {vtkInteractorStyle style = vtkInteractorStyle.New();style.LeftB…...
Linux-交换空间(Swap)管理
引入概念 在计算机中,硬盘的容量一般比内存大,内存(4GB 8GB 16GB 32GB 64GB…),硬盘(512GB 1T 2T…)。 冯诺依曼的现代计算机结构体系里面的存储器就是内存 内存是一种易失性存储器,…...
扫描某个网段下存活的IP:fping
前言: 之前用arp统计过某网段下的ip,但是有可能统计不全。网络管理平台又不允许登录。想要知道当前的ip占用情况,可以使用fping fping命令类似于ping,但比ping更强大。与ping需要等待某一主机连接超时或发回反馈信息不同&#x…...
【深度学习】PyTorch框架(3):优化与初始化
1.引言 在本文中,我们将探讨神经网络的优化与初始化技术。随着神经网络深度的增加,我们会遇到多种挑战。最关键的是确保网络中梯度流动的稳定性,否则可能会遭遇梯度消失或梯度爆炸的问题。因此,我们将深入探讨以下两个核心概念&a…...
Go-知识测试-子测试
Go-知识测试-子测试 1. 介绍2. 例子3. 子测试命名规则4. 选择性执行5. 子测试并发6. testing.T.Run7. testing.T.Parallel8. 子测试适用于单元测试9. 子测试适用于性能测试10. 总结10.1 启动子测试 Run10.2 启动并发测试 Parallel 建议先看:https://blog.csdn.net/a…...
.net core IConfiguration 读 appsettings.json 数据,举例
在.NET Core中,IConfiguration 接口是用来读取配置数据的,包括从 appsettings.json 文件中读取。下面是一个如何在使用.NET Core时通过 IConfiguration 读取 appsettings.json 数据的示例。 首先,假设你的 appsettings.json 文件内容如下&am…...
全球Windows机器蓝屏,作为量化人,我的检讨来了
昨天下午,微软给大家放了个假。Windows又双叒死机了。不过,这一次不是几台机器,而是全球大范围宕机。这一刻,大家都是“正蓝旗”。 蓝瓶的,效果好! 现在根本原因已经找到,绝大多数人的机器都已修…...
部署和运维
目录 1.Git1.1. Git指令中merge和rebase的区别1. Commit 记录2. 合并方式3. 冲突处理4. 使用场景选择建议 1.2. cherry-pick的使用如何使用 git cherry-pick例子处理冲突撤销 cherry-pick其他选项 结论 2. 部署1. Nginx的使用场景 编译打包1. webpack2. webpack打包优化1. 代码…...
微信小程序基本语法
官网 https://developers.weixin.qq.com/miniprogram/dev/framework/ 视频教程:尚硅谷微信小程序开发教程,2024最新微信小程序项目实战! 仿慕尚花坊项目源码:https://gitee.com/abcdfdewrw/flower-workshop 目录 一,初…...
测试用例的设计方法
等价类 等价类概念:在所有测试的数据中,具有某种共同特征的数据子集 边界值 边界值分析是对程序输入或输出的边界值进行测试的一种黑盒测试方法 边界值是作为等价类的补充,其主要区别是: 边界值测试设计不是从某一个等价类中…...
Android10.0 锁屏分析-KeyguardPatternView图案锁分析
首先一起看看下面这张图: 通过前面锁屏加载流程可以知道在KeyguardSecurityContainer中使用getSecurityView()根据不同的securityMode inflate出来,并添加到界面上的。 我们知道,Pattern锁所使用的layout是 R.layout.keyguard_pattern_view&a…...
Python 装饰器:函数的函数,代码的艺术
引言 在Python中,装饰器是一种强大的功能,允许程序员在不修改原函数源码的情况下增强或修改函数行为。装饰器本质上是一个接收函数作为参数的高阶函数,并返回一个新的函数或修改原函数的行为。这种机制极大地提高了代码的复用性、可读性和模…...
安全防御2
实验要求: 实验过程: 7,办公区设备可以通过电信链路和移动链路上网(多对多的NAT,并且需要保留一个公网IP不能用来转换): 新建电信区: 新建移动区: 将对应接口划归到各自区域: 新建…...
C语言 ——— 打印水仙花数
目录 何为水仙花数 题目要求 代码实现 何为水仙花数 “水仙花数”是指一个n位数,其各位数字的n次方之和等于该数本身 如:153 1^3 5^3 3^3,则153就是一个“水仙花数” 题目要求 求出0~100000的所有“水仙花数”并输出 代码实现 #i…...
「Conda」在Linux系统中安装Conda环境管理器
在Linux系统中安装Conda环境管理器是一个相对简单的过程。 1. 准备工作 确保你的Linux系统已经更新到最新版本,并安装了基本的开发工具和库。打开终端,执行以下命令: sudo apt-get update sudo apt-get upgrade sudo apt-get install build-essential2. 安装Miniconda或An…...
9.11和9.9哪个大?GPT-4o也翻车了
今天刷到了这个问题,心血来潮去问下chatgpt-4o,没想到疯狂翻车... 第一次问: GPT一开始给出了难绷的解答,让我想起了某短视频软件评论区里对某歌手节目排名的质疑哈哈哈哈哈 但是在接下来的进一步询问和回答中它反应过来了。 第…...
[开源]语雀+Vercel:打造免费个人博客网站
大家好,我是白露。 今天我想和大家分享我的今年的第一个开源项目 —— 基于语雀+Nextjs+Vercel实现免费的博客系统。 简单来说,你在语雀写博客,然后直接一键同步到个人网站上,网站自动部署! 而且,整个过程几乎不需要额外的成本,也不用充值语雀超级会员,hh。这个项目…...
使用ElementUI和element-china-area-data库实现省市区三级联动组件封装
在前端开发中,省市区三级联动是一个常见的需求。今天我们将使用Vue.js和ElementUI组件库,结合element-china-area-data库,来实现一个省市区三级联动的组件。这个组件不仅可以提高用户体验,还能大大简化我们的代码。接下来…...
thinkphp企业网站开发/网络推广渠道有哪些
比如下面的代码中实现了一个只能转换User对象的MessageConverter,底层使用的是FastJson,在进行发送消息时重置了user的name属性,加上了t-前缀。 然后为了使它生效,我们需要把它定义为一个bean,并标注StreamMessageConv…...
wordpress页面生成/苏州旺道seo
大家好,我是你们的码农大哥——栈长。 6 月初的时候给大家介绍了 Spring 团队的最新杀手锏项目:Spring Native,它的存在就是干掉 JVM,另起一个 JVM 之外的生态,上篇也简单实战了一下,相信大家都有了一个全…...
成都网站设计开发公司/百度搜索引擎广告位的投放
Ubuntu 3D目录了解 Ubuntu历史与发展过程特色Ubuntu的3D桌面开发意念安装设定从硬盘安装其他特色软件组件缺省套件私有版权软件的采用发布周期正式衍生版本非正式衍生版本中文译名各界评价免费获取Ubuntu CD软件组件缺省套件私有版权软件的采用发布周期正式衍生版本非正式衍生版…...
做任务的电脑网站/福州seo网站推广优化
爬取网页数据是python很长干的一件事情,不过做起来基本上都是很冗长的一段代码,看起来复杂,不宜理解。今天给大家分享一个小诀窍,利用python3中的requests类库进行爬取网页数据。我们先看一哈用这个requests类库做的效果本节分享技…...
做网站软件流程/武汉百度推广外包
写原生的时候,我们经常会用到广播,接口,回调等方法来实现发送和接受通知以及通信的。 那么在RN中,也有一套发送和接收通知的方法,用的组件是DeviceEventEmitter。下面看一下,RN中是如何发送和接收事件的&am…...
什么叫网站建设方案书/中国疫情最新数据
zookeeper是一个开源的分布式协调服务,是由雅虎创建的,基于google chubby。是一种分布式数据一致性的解决方案。一、zookeeper的特性顺序一致性 从同一个客户端发起的事务请求,最终会严格按照顺序被应用到zookeeper中。原子性 所有的事务请求…...