C++(进阶) 第2章 多态
C++(进阶) 第2章 多态
文章目录
- 前言
- 一、多态的概念
- 二、多态的定义及实现
- 1.虚函数
- 2.虚函数的重写
- 3.多态的条件
- 4.多态的细节
- 三、析构函数的重写
- 四、重载/重写/隐藏的对比
- 五、抽象类
- 抽象类
- 六、相关题目
- 题目1
- 题目2
- 七、const修饰
- 八、多态原理
- 九、虚函数放在地方
- 总结
前言
什么是多态?多态其实就是多种形态的简写这篇博客会详细的介绍多态
一、多态的概念
多态(polymorphism)的概念:通俗来说,就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态),这⾥我们重点讲运⾏时多态,编译时多态(静态多态)和运⾏时多态(动态多态)。编译时多态(静态多态)主要就是我们前⾯讲的函数重载和函数模板,他们传不同类型的参数就可以调⽤不同的函数,通过参数不同达到多种形态,之所以叫编译时多态,是因为他们实参传给形参的参数匹配是在编译时完成的,我们把编译时⼀般归为静态,运⾏时归为动态。
通俗点来说就是不同的东西做不同的事情有多种形态
二、多态的定义及实现
1.虚函数
类成员函数前⾯加virtual修饰,那么这个成员函数被称为虚函数。注意⾮成员函数不能加virtual修饰。
这是构成多态的必备条件之一
class Person{public:virtual void BuyTicket() { cout << "买票-全价" << endl; }};
2.虚函数的重写
这里学生类继承了person类那么这里按之前的理解应该构成隐藏关系但是这里不一样,这里我们一般叫做重写
class Person{public:virtual void BuyTicket() { cout << "买票-全价" << endl; }};
class Student : public Person
{
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};
假如我现在要去买票
class Person{public:virtual void BuyTicket() { cout << "买票-全价" << endl; }};
class Student : public Person
{
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};void func(Person& p)
{p.BuyTicket();
}
int main()
{Person p;Student s;func(p);func(s);return 0;
}
注意看这里传入的是父类

这里可能会有疑问为什么func函数参数写的父类但是这里传入子类也可以这是因为切片

这里可以看到传入不同的类型处理的方式也不一样这里就是一个简单的多态
3.多态的条件
- 虚函数的重写
- 必须是父类的指针或者是引用
- 上面俩个条件缺一不可
4.多态的细节
假如这里把父类的virtual去掉这里是多态吗?
class Person{public:void BuyTicket() { cout << "买票-全价" << endl; }};
class Student : public Person
{
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};void func(Person& p)
{p.BuyTicket();
}
int main()
{Person p;Student s;func(p);func(s);return 0;
}
这里的答案是:不是多态

假如这里子类去掉virtual是多态吗?

答案是:是多态

面试题就很喜欢这样考,但是这里是为什么父类去就不行子类就行呢?
上面那样构成虚函数的重写就算不写virtual,因为继承了父类子类哪里会把父类的声明拿下来也是属于多态
多态调用:看指向对象类型,指向谁调用谁的虚函数
普通调用:看调用者的类型,调用调用者的函数
三、析构函数的重写
假如这里有1父1子
class person
{
public:~person(){cout << "~person()" << endl;}};class student:public person
{
public:~student(){cout << "~student()" << endl;}
};int main()
{person* p1 = new student;delete p1;
}

这里定义了一个指针指向了student这里

但是这里调用的是父类的析构函数
上面这里会出现非常大的问题上面的代码是为了方便理解
假如我把代码改成这样
class person
{
public:~person(){cout << "~person()" << endl;}};class student:public person
{
public:~student(){delete[] _ptr;cout << "~student()" << endl;}
protected:int* _ptr = new int[10];
};int main()
{person* p1 = new student;delete p1;
}

可以看到这里就出现了很严重的内存泄露
按正常关系来说studen继承了person,这studen就应该有俩个析构函数一个自己的一个父类的但是这里为什么只调用了一个
这里是因为,析构函数会被编译器统一处理成destructor()那么这里就有俩个destructor函数这里就会成隐藏关系
所以这里就只会调用父类的析构函数
这里的解决方式就是加上virtual


四、重载/重写/隐藏的对比

五、抽象类
抽象类
class Car
{
public:virtual void Drive() = 0;
};
这就是一个抽象类,在虚函数的后⾯写上 =0 ,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被派⽣类重写,但是语法上可以实现),只要声明即可。
抽象类也无法实例化,这里就会有一个疑问了,那有个蛋用呢?

如果我不想让别人实例化出car类出来但是可以实例化benz出来就可以用抽象派去重写,继承的benz类是可以实例化的
class Car
{
public:virtual void Drive() = 0;
};class Benz : public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl; }
};
int main()
{Benz a;
}
先来俩题热热身
六、相关题目
题目1
class A
{
public:virtual void func(int val = 1){cout << "A->" << val << endl;}virtual void test(){func();}
};class B : public A
{
public:void func(int val = 0){cout << "B->" << val << endl;}
};int main()
{B* p = new B;p->test();return 0;}

答案是B

这里就是c++语法的一个大坑,用的是B类,但是这里是继承的test会去找子类的,但是可以发现子类哪里是可以不写virtual的,用的是子类的定义,但是声明依旧用的是父类的
题目2
class Base
{
public:virtual void Fun1(){cout << "Func1" << endl;}
private:int _a = 1;char ch = '1';};int main()
{cout << sizeof(Base) << endl;
}


定义成虚函数了以后这里就多要一个指针

所以说写成虚函数就会付出代价,这个指针指向虚函数表,虚函数的地址都会放在这张表里面,多态就是靠这张虚函数表实现的
七、const修饰
纯虚函数无法实例化,这里可以强制别人用引用或者指针
class Animal
{
public:virtual void sound() = 0;
};class Cat : public Animal
{
public:virtual void sound(){cout << "Cat-喵喵" << endl;}
};
class Dog : public Animal
{
public:virtual void sound(){cout << "Dog-汪汪" << endl;}
};
void AnimalSound(Animal a)
{}

这里加上const可以使用匿名对象
class Animal
{
public:virtual void sound() const = 0;
};class Cat : public Animal
{
public:virtual void sound()const {cout << "Cat-喵喵" << endl;}
};
class Dog : public Animal
{
public:virtual void sound()const {cout << "Dog-汪汪" << endl;}
};
void AnimalSound(const Animal& a)
{a.sound();
}
int main()
{AnimalSound(Cat());return 0;
}

八、多态原理
上面说到多态必须是要虚函数并且形参还需要是父类的引用或者指针,那么这个虚函数和普通的函数有什么区别呢?

这里可以看到这里除了成员变量_a外还有一个_vfptr,这里面还放着成员函数的,这个东西其实就是一个指针数组,这个指针会指向一个表这个表就是虚表
下面俩个是虚函数一个不是,这里用监视窗口查看一下
class Base
{
public:virtual void Func1(){cout << "Base::Func1" << endl;}virtual void Func2(){cout << "Base::Func2" << endl;}void Func3(){cout << "Base::Func3" << endl;}
private:int _a;};int main()
{Base b;return 0;
}

这里可以通过上面看到这个表里面只有俩个函数
用内存窗口是可以看见的

多态其实就是动态确定地址
举个例子
class Base
{
public:virtual void Func1(){cout << "Base::Func1" << endl;}virtual void Func2(){cout << "Base::Func2" << endl;}void Func3(){cout << "Base::Func3" << endl;}
private:int _a;};
class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1" << endl;}
private:int _b;
};
void Func1(Base* p)
{p->Func1();//虚函数p->Func3();//不是虚函数
}int main()
{Func1(new Base);Func1(new Derive);return 0;
}


这里可以看见func1是重写后的那个func1但是这里的func3是不会重写的
第二个func1是重写后的那个func也就是谁调用那个虚表就指向,
第二个调用的时候我这里给的参数是Derive,这里会发生切片,这里就会对func1进行重写,但是这里收到的参数始终都是一个base指针,只不过这里可能是子类切片出来的,切片和不是切片出来的这里就会出现虚表地址不同
所以这里就叫运行时绑定也叫多态绑定,这里就可以指向父类调用父类,指向子类就调用子类,它能调用不同的函数,它本质的原因就是因为那个虚表的地址不同
这也就是为什么函数的参数必需要父类的指针或者引用的原因

九、虚函数放在地方
- 虚函数 ------------代码段
- 虚函数表 --------代码段
- 虚函数表的指针-类对象
总结
多态看起来复杂其实只要理解了虚表这个东西就不会复杂,虚函数就有一个指针数组,这里面放着虚表的地址,然后这些虚表里面放着虚函数的地址,然后在通过切片来看是哪一个虚表的地址这就是多态的原理
相关文章:
C++(进阶) 第2章 多态
C(进阶) 第2章 多态 文章目录 前言一、多态的概念二、多态的定义及实现1.虚函数2.虚函数的重写3.多态的条件4.多态的细节 三、析构函数的重写四、重载/重写/隐藏的对比五、抽象类抽象类 六、相关题目题目1题目2 七、const修饰八、多态原理九、虚函数放在地方总结 前…...
mac删除程序坞(Dock)中“无法打开的程序“
参考: Mac删除软件之后图标还在怎么办?https://blog.csdn.net/weixin_46500474/article/details/124284161Mac程序坞中软件删除出现残留“?”图标无法删除解决方法: https://blog.csdn.net/shenwenhao1990/article/details/12865…...
【Linux】vi/vim 使用技巧
文章目录 1. 简介vi和vim的历史vi和vim的区别安装vimUbuntu/DebianCentOS/RHELFedoramacOSWindows 2. 基本操作启动和退出启动退出 模式介绍普通模式插入模式命令模式 光标移动基本移动高级移动 3. 文本编辑插入文本删除文本复制和粘贴撤销和重做 4. 搜索与替换基本搜索搜索文本…...
Python自动化办公(系统维护及开发任务状态自动推送)
Python自动化办公, 1.需求分析 系统维护及开发人员的工作一般都会比较繁杂,领导们喜欢实时掌控项目的进度,但是领导们很多时候是不会自己主动去查看及分析项目进度数据的,干活的牛马们也没空整天日报,周报,月报,季报,年报…活又有了,又该想想怎么干,需求的核心是实现自动整理…...
CentOS7 Apache安装踩坑
Gnome桌面右键弹出终端。 [rootlocalhost ~]# yum repolist 已加载插件:fastestmirror, langpacks /var/run/yum.pid 已被锁定,PID 为 2611 的另一个程序正在运行。 Another app is currently holding the yum lock; waiting for it to exit... [root…...
OpenMMlab导出MaskFormer/Mask2Former模型并用onnxruntime和tensorrt推理
onnxruntime推理 使用mmdeploy导出onnx模型: from mmdeploy.apis import torch2onnx from mmdeploy.backend.sdk.export_info import export2SDK# img ./bus.jpg # work_dir ./work_dir/onnx/maskformer # save_file ./end2end.onnx # deploy_cfg ./configs/m…...
若依微服务中配置 MySQL + DM 多数据源
文章目录 1、导入 MySQL 和达梦(DM)依赖2、在 application-druid.yml 中配置达梦(DM)数据源3、在 DruidConfig 类中配置多数据源信息4、在 Service 层或方法级别切换数据源4.1 在 Service 类上切换到从库数据源4.2 在方法级别切换…...
一些前端组件介绍
wangEditor : 一款开源 Web 富文本编辑器,可用于 jQuery Vue React等 https://www.wangeditor.com/ Handsontable:一款前端可编辑电子表格https://blog.csdn.net/carcarrot/article/details/108492356mitt:Mitt 是一个在 Vue.js 应…...
python学opencv|读取图像(九)用numpy创建黑白相间灰度图
【1】引言 前述学习过程中,掌握了用numpy创建矩阵数据,把所有像素点的BGR取值设置为0,然后创建纯黑灰度图的方法,具体链接为: python学opencv|读取图像(八)用numpy创建纯黑灰度图-CSDN博客 在…...
AtCoder Beginner Contest 383
C - Humidifier 3 Description 一个 h w h \times w hw 的网格,每个格子可能是墙、空地或者城堡。 一个格子是好的,当且仅当从至少一个城堡出发,走不超过 d d d 步能到达。(只能上下左右走,不能穿墙)&…...
20. 内置模块
一、random模块 random 模块用来创建随机数的模块。 random.random() # 随机生成一个大于0且小于1之间的小数 random.randint(a, b) # 随机生成一个大于等于a小于等于b的随机整数 random.uniform(a, b) …...
《知识拓展 · 统一建模语言UML》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
计算机网络-Wireshark探索ARP
使用工具 Wiresharkarp: To inspect and clear the cache used by the ARP protocol on your computer.curl(MacOS)ifconfig(MacOS or Linux): to inspect the state of your computer’s network interface.route/netstat: To inspect the routes used by your computer.Brows…...
减少30%人工处理时间,AI OCR与表格识别助力医疗化验单快速处理
在医疗行业,化验单作为重要的诊断依据和数据来源,涉及大量的文字和表格信息,传统的手工输入和数据处理方式不仅繁琐,而且容易出错,给医院的运营效率和数据准确性带来较大挑战。随着人工智能技术的快速发展,…...
1.2.3计算机软件
一个完整的计算机系统由硬件和软件组成,用户使用软件,而软件运行在硬件之上,软件进一步的划分为两类:应用软件和系统软件。普通用户通常只会跟应用软件打交道。应用软件是为了解决用户的某种特定的需求而研发出来的。除了每个人都…...
二、uni-forms
避坑指南:uni-forms表单在uni-app中的实践经验-CSDN博客...
Android13开机向导
文章目录 前言需求-场景第三方资料说明需求思路按照平台 思路 从配置上去 feature换个思路,去feature。SimMissingActivity 判断跳过逻辑SetupWizardUtils 判断SIM 、 hasSystemFeature FEATURE_TELEPHONYPackageManager.FEATURE_TELEPHONYApplicationPackageManage…...
软件测试丨Appium 源码分析与定制
在本文中,我们将深入Appium的源码,探索它的底层架构、定制化使用方法和给软件测试带来的优势。我们将详细介绍这些技术如何解决实际问题,并与大家分享一些实用的案例,以帮助读者更好地理解和应用这一技术。 Appium简介 什么是App…...
1.网络知识-IP与子网掩码的关系及计算实例
IP与子网掩码 说实话,之前没有注意过,今天我打开自己的办公地电脑,看到我的网络配置如下: 我看到我的子网掩码是255.255.254.0,我就奇怪了,我经常见到的子网掩码都是255.255.255.0啊?难道公司配…...
Android中Gradle常用配置
前言 本文记录了一些常用的gradle配置,基本上都是平时开发中可能会使用到的,如果有新内容会不定时更新,附官网 1.依赖库版本写法 不推荐写法: dependencies {compile com.example.code.abc:def:2. // 不推荐的写法 }这样写虽然可…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
深入理解 React 样式方案
React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...
统计学(第8版)——统计抽样学习笔记(考试用)
一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征(均值、比率、总量)控制抽样误差与非抽样误差 解决的核心问题 在成本约束下,用少量样本准确推断总体特征量化估计结果的可靠性(置…...
开疆智能Ethernet/IP转Modbus网关连接鸣志步进电机驱动器配置案例
在工业自动化控制系统中,常常会遇到不同品牌和通信协议的设备需要协同工作的情况。本案例中,客户现场采用了 罗克韦尔PLC,但需要控制的变频器仅支持 ModbusRTU 协议。为了实现PLC 对变频器的有效控制与监控,引入了开疆智能Etherne…...
