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

【C++】多态

多态

  • 一、多态的概念及定义
    • 1.1 虚函数
    • 1.2 虚函数重写的特殊情况
    • 1.3 override 和 final
  • 二、抽象类
    • 2.1 概念
    • 2.2 用处
  • 三、多态的原理
    • 3.1 虚函数表
      • 3.1.1 虚函数与虚表的位置
    • 3.2 多态的原理
    • 3.3 静态绑定和动态绑定
  • 四、单/多继承的虚函数表
    • 4.1 单继承的虚函数表
    • 4.2 多继承的虚函数表

一、多态的概念及定义

多态:

去完成某个行为,当不同的对象去完成时会产生出不同的状态。

构成多态有两个条件:

1️⃣ 必须是虚函数的重写
2️⃣ 必须通过父类的指针或者引用调用虚函数

隐藏/重定义的条件:函数名相同
重写/覆盖的条件:函数名、返回值、参数都相同且是虚函数

1.1 虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数

class A
{
public:// 虚函数virtual void Print(){cout << "A" << endl;}
};class B : public A
{
public:// 虚函数的重写/覆盖virtual void Print(){cout << "B" << endl;}
};int main()
{A a;B b;A* pa = &a;A* pb = &b;pa->Print();pb->Print();return 0;
}

结果:

A
B

结论:
普通调用跟的调用的对象的类型有关
多态调用跟指针/引用指向的对象有关

1.2 虚函数重写的特殊情况

1️⃣ 子类的虚函数可以不加virtual

因为子类对象会对父类继承下来的虚函数进行重写。

1️⃣ 协变

返回值可以不同,但必须是父子类关系的指针或引用,父亲就用父类对象,子类就用子类对象。

3️⃣ 析构函数

class A
{
public:~A(){cout << "~A() " << _a << endl;delete []_a;}int* _a = new int[20];
};class B : public A
{
public:~B(){cout << "~B() " << _b << endl;delete[]_b;}int* _b = new int[20];
};int main()
{A* a = new A;A* b = new B;delete a;delete b;return 0;
}

结果:

~A() 015E8820
~A() 015E9738

可以看到发生了内存泄漏delete有两个操作:1、使用指针调用析构函数。2、operator delete()
而因为析构函数没有用virtual所以是普通调用,只与类型有关,全部调用的是A的析构函数。

所以建议析构函数也加上virtual。

1.3 override 和 final

final关键字在父类修饰虚函数,表示该虚函数不能再被重写

class A
{
public:// 虚函数virtual void Print() final{cout << "A" << endl;}
};class B : public A
{
public:// 不能被重写virtual void Print(){cout << "B" << endl;}
};

override在子类修饰虚函数,检查子类是否重写,如果没有重写则编译报错

class A
{
public:// 虚函数virtual void Print(){cout << "A" << endl;}
};class B : public A
{
public:// 检查是否被重写virtual void Print() override{cout << "B" << endl;}
};

二、抽象类

2.1 概念

在虚函数的后面写上 =0 ,则这个函数为纯虚函数包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。 派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

class A
{
public:// 纯虚函数virtual void Print() = 0;
};class B : public A
{
public:virtual void Print(){cout << "B" << endl;}
};int main()
{A a; // yesB b; // noreturn 0;
}

2.2 用处

当不能定义出具体的类的时候就可以用抽象类,比如说车的品牌,酒的不同种类。
或者当想要强制重写虚函数的时候也可以使用。

三、多态的原理

3.1 虚函数表

class A
{
public:virtual void Print() {}int _a;
};int main()
{A a;cout << sizeof(a) << endl;return 0;
}

结果:

8

出现这种结果的原因是因为有__vfptr虚表指针。虚表指针放的是虚函数的地址。

class A
{
public:virtual void Print1() {}virtual void Print2() {}void Print3() {}int _a;
};int main()
{A a;cout << sizeof(a) << endl;return 0;
}

结果依然不变,是8
Print1和Print2会放进虚函数表中:
在这里插入图片描述

class A
{
public:virtual void Print1() {cout << "A::Print1()" << endl;}virtual void Print2(){cout << "A::Print2()" << endl;}void Print3(){cout << "A::Print3()" << endl;}int _a = 0;
};class B : public A
{
public:virtual void Print1(){cout << "B::Print1()" << endl;}int _b = 0;
};int main()
{A a;B b;return 0;
}

在这里插入图片描述
可以看出子类先拷贝了父类的虚表,完成重写的虚函数,虚表对应的位置覆盖成重写的虚函数

3.1.1 虚函数与虚表的位置

注意

虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。而虚表存的位置也是代码段

3.2 多态的原理

为什么能做到指向父类对象的指针调用的是父类函数,指向子类就调用子类函数。
原因是运行时会找虚函数表,虚函数表就是一个函数指针,来找到对应的虚函数,这种操作叫做动态绑定。

3.3 静态绑定和动态绑定

静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,
比如:函数重载
动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体
行为
,调用具体的函数,也称为动态多态。

四、单/多继承的虚函数表

4.1 单继承的虚函数表

class A
{
public:virtual void Print1() {cout << "A::Print1()" << endl;}virtual void Print2(){cout << "A::Print2()" << endl;}int _a = 0;
};class B : public A
{
public:virtual void Print1(){cout << "B::Print1()" << endl;}virtual void Print3(){cout << "B::Print3()" << endl;}void Print4(){cout << "B::Print4()" << endl;}int _b = 0;
};typedef void(*VfPtr)();void VfPrint(VfPtr vft[])// 打印虚表
{for (int i = 0; vft[i]; i++){printf("[%d]:%p->", i, vft[i]);vft[i]();}cout << endl;
}int main()
{A a;B b;VfPrint((VfPtr*)(*(int*)&a));VfPrint((VfPtr*)(*(int*)&b));return 0;
}

结果:
在这里插入图片描述
可以看出子类如果有自己的虚函数会放到虚表的最后。

4.2 多继承的虚函数表

class A
{
public:virtual void Print1() {cout << "A::Print1()" << endl;}virtual void Print2(){cout << "A::Print2()" << endl;}int _a = 0;
};class B : public A
{
public:virtual void Print1(){cout << "B::Print1()" << endl;}virtual void Print2(){cout << "B::Print2()" << endl;}int _b = 0;
};class C : public A, public B
{
public:virtual void Print1(){cout << "C::Print1()" << endl;}virtual void Print3(){cout << "C::Print3()" << endl;}int c = 0;
};typedef void(*VfPtr)();void VfPrint(VfPtr vft[])// 打印虚表
{for (int i = 0; vft[i]; i++){printf("[%d]:%p->", i, vft[i]);vft[i]();}cout << endl;
}int main()
{A a;B b;C c;VfPrint((VfPtr*)(*(int*)&a));VfPrint((VfPtr*)(*(int*)&b));// 第一张虚表VfPrint((VfPtr*)(*(int*)&c));// 第二张虚表VfPrint((VfPtr*)(*(int*)((char*)&c + sizeof(A))));return 0;
}

对象c有两张虚表(从a和b中继承下来的)。注意多继承不一定有多张虚表,因为有可能有的类没有虚函数。

在这里插入图片描述
在这里插入图片描述
可以看出多继承中子类虚函数会加到第一张虚表中。

相关文章:

【C++】多态

多态一、多态的概念及定义1.1 虚函数1.2 虚函数重写的特殊情况1.3 override 和 final二、抽象类2.1 概念2.2 用处三、多态的原理3.1 虚函数表3.1.1 虚函数与虚表的位置3.2 多态的原理3.3 静态绑定和动态绑定四、单/多继承的虚函数表4.1 单继承的虚函数表4.2 多继承的虚函数表一…...

分布式项目-品牌管理(5、6)

【今日成果】&#xff1a; //使用阿里云OSS服务&#xff1a; //使用v-if如果地址没有就不显示 &#xff0c; 如果地址错误图片就显示不出来&#xff1b; 【快速回顾】&#xff1a; 任何数据的删除都不要使用物理上的删除&#xff0c;应当使用逻辑上的删除&#xff01;&…...

自定义ESLint规则开发与使用

自定义eslint及使用 项目结构 |-eslint-plugin-demo //自定义eslint插件项目 | |-demo-app // 使用自定义eslint的测试应用 |-README.md 项目效果&#xff1a; github项目地址 自定义ESLint环境准备 安装脚手架 执行下列命令来安装开发eslint的脚手架。 yo(y…...

【JavaScript】35_包装类与垃圾回收机制

10、包装类 在JS中&#xff0c;除了直接创建原始值外&#xff0c;也可以创建原始值的对象 通过 new String() 可以创建String类型的对象 通过 new Number() 可以创建Number类型的对象 通过 new Boolean() 可以创建Boolean类型的对象 但是千万不要这么做 包装类&#xff1…...

【CS224W】(task3)NetworkX工具包实践

note 节点可以为任意可哈希的对象&#xff0c;比如字符串、图像、XML对象&#xff0c;甚至另一个Graph、自定义的节点对象。通过这种方式可以自由灵活地构建&#xff1a;图为节点、文件为节点、函数为节点&#xff0c;等灵活的图形式。暂时省略&#xff1a;【B5】计算机网络图…...

ansible的模块详解

ansible 的概述 什么是ansible Ansible是一款为类Unix系统开发的自由开源的配置和自动化工具。 它用Python写成&#xff0c;类似于saltstack和Puppet&#xff0c;但是有一个不同和优点是我们不需要在节点中安装任何客户端。 它使用SSH来和节点进行通信。Ansible基于 Python…...

《Terraform 101 从入门到实践》 Functions函数

《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新&#xff0c;书中的示例代码也是放在GitHub上&#xff0c;方便大家参考查看。 Terraform的函数 Terraform为了让大家在表达式上可以更加灵活方便地进行计算&#xff0c;提供了大量的内置函数…...

使用kubeadm快速部署一个K8s集群

wkubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。 这个工具能通过两条指令完成一个kubernetes集群的部署&#xff1a; # 创建一个 Master 节点 $ kubeadm init# 将一个 Node 节点加入到当前集群中 $ kubeadm join <Master节点的IP和端口 >1. 安装要求 …...

初探富文本之CRDT协同算法

初探富文本之CRDT协同算法 CRDT的英文全称是Conflict-free Replicated Data Type&#xff0c;最初是由协同文本编辑和移动计算而发展的&#xff0c;现在还被用作在线聊天系统、音频分发平台等等。当前CRDT算法在富文本编辑器领域的协同依旧是典型的场景&#xff0c;常用于作为…...

Dubbo和Zookeeper集成分布式系统快速入门

文件结构 代码部分 1、新建provider-server导入pom依赖 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version></dependency><dependency>&l…...

大数据工具Maxwell的使用

1.Maxwell简介 Maxwell 是由美国Zendesk公司开源&#xff0c;用Java编写的MySQL变更数据抓取软件。它会实时监控Mysql数据库的数据变更操作&#xff08;包括insert、update、delete&#xff09;&#xff0c;并将变更数据以 JSON 格式发送给 Kafka、Kinesi等流数据处理平台。 官…...

freesurfer如何将组模板投影到个体空间——如投影 Schaefer2018 到个体空间

freesurfer如何将组模板投影到个体空间——如投影 Schaefer2018 到个体空间 freesurfer如何将组模板投影到个体空间? freesurfer如何将组模板投影到个体空间——如投影 Schaefer2018 到个体空间freesurfer的整理流程freesurfer的安装freesurfer对结构像分割流程及批处理代码fr…...

Matlab傅里叶谱方法求解二维波动方程

傅里叶谱方法求解基本偏微分方程—二维波动方程 二维波动方程 将一维波动方程中的一维无界弦自由振动方程推广到二维空间上, 就得到了描述无界 (−∞<x,y<∞)(-\infty<x, y<\infty)(−∞<x,y<∞) 弹性薄膜的波动方程: ∂2u∂t2a2(∂2∂x2∂2∂y2)u(1)\frac…...

【深度学习】卷积神经网络

1 卷积神经网络&#xff08;CNN&#xff09;可以做什么&#xff1f; 检测任务分类与检索超分辨率重构&#xff1a;将图像训练的更清晰医学任务等无人驾驶人脸识别 2 用GPU&#xff1a;图像处理单元 比CPU块一百倍以上 3 卷积神经网络与传统神经网络的区别 传统神经网络&…...

【C++】六个默认成员函数——取地址重载,const成员函数

&#x1f345; 初始化和清理 拷贝复制 目录 ☃️1.取地址重载 ☃️2.const取地址操作符重载 这两个运算符一般不需要重载&#xff0c;使用编译器生成的默认取地址的重载即可&#xff0c;只有特殊情况&#xff0c;才需要重载&#xff0c;比如想让别人获取到指定的内容&#xf…...

Win11浏览器无法上网,秒杀网上99.9%教程—亲测完胜

前言 例如&#xff1a;网上的教程 列如&#xff1a; 关闭代理服务器、QQ微信可以登录&#xff0c;但浏览器无法上网、Win11、Win10无法上网、重启网络、重启电脑、去掉代理服务器等等。 一系列教程&#xff0c;要多鸡肋就多鸡肋。 我是用我2020年在CSDN上发布的第一篇文章&…...

Vulkan Graphics pipeline Dynamic State(图形管线之动态状态)

Vulkan官方英文原文&#xff1a;请见 Vulkan 1.3.236 - A Specification 10.9 章节。对应的Vulkan技术规格说明书版本&#xff1a; Vulkan 1.3.2A dynamic pipeline state is a state that can be changed by a command buffer command during the execution of a command buff…...

CSP-《I‘m stuck!》-感悟

题目 做题过程 注&#xff1a;黄色高亮表示需要注意的地方&#xff0c;蓝色粗体表示代码思路 好久没有写过代码了&#xff0c;今天做这道编程题&#xff0c;简直是灾难现场。 上午编程完后发现样例没有通过&#xff0c;检查发现算法思路出现了问题&#xff1a;我计数了S不能到…...

[实践篇]13.19 Qnx进程管理slm学习笔记(二)

【QNX Hypervisor 2.2用户手册】目录(完结) 四,配置文件结构 4.1 根元素 一个配置文件的XML根元素是system,如下: <SLM:system>-- component and module descriptions -- </SLM:system> 4.2 组件 一个进程对于SLM来说就是一个组件。在配置文件中,你必须为一…...

(免费分享)基于 SpringBoot 的高校宿舍管理系统带论文

项目描述 系统代码质量高&#xff0c;功能强大&#xff0c;带论文。 系统的功能主要有&#xff1a; &#xff08;1&#xff09;基本信息管理 基本信息分为学生信息和宿舍信息两部分&#xff0c;其功能是负责维护这些信息&#xff0c;对 它们进行增删查改等操作。 &#x…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...