【C++进阶(九)】C++多态深度剖析
💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:C++从入门到精通⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你学习C++
🔝🔝
多态
- 1. 前言
- 2. 多态的概念以及定义
- 3. 多态的实例调用情况
- 4. 构成多态的两个特例
- 5. 多态的底层原理分析(一)
- 6. 多态底层原理分析(二)
- 7. 多态中的两个关键字
- 8. 抽象类以及虚函数的几个结论
- 9. 总结以及拓展
1. 前言
继承和多态这两兄弟常常一起出现
继承是实现多态的前提!
本章重点:
本篇文章着重讲解多态的概念以及
定义,多态的底层原理和析构函数重写
以及函数重写的两个例外条件
多继承中的虚函数表关系.其中,简单介绍
的部分有抽象类的概念以及定义和
继承与多态中的两个新增关键字
注:如果你不知道什么是继承,或继承
的知识掌握不牢固,请先阅读下面文章:
C++继承深度剖析
2. 多态的概念以及定义
概念:
通俗来说,多态就是多种状态
父子对象完成相同任务会产生不同的结果
比如:
学生和普通人都去买门票
学生是半价,而普通人是全价
在继承中构成多态要有两个条件:
- 必须通过基类的指针或引用调用虚函数
- 被调用的函数必须是虚函数
并且子类的虚函数要被重写
现在的你可能有一万个问号
什么是虚函数?什么是重写?
没关系,我们一步一步讲!
关键字virtual加在成员函数前
这个成员函数就是虚函数!
虚函数的重写(也叫覆盖)
:
派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的,返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
上面的代码中,BuyTicket函数就被重写了!
概念讲完,下一步进行实战!
3. 多态的实例调用情况
构成多态的条件就两个,一定要熟记!
一定要熟记!一定要熟记!重要的事情说三遍
下面是多态的实例:
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};int main()
{Person* p1 = new Person;Person* p2 = new Student;p1->BuyTicket();p2->BuyTicket();return 0;
}
我们知道一个事实:
基类的指针或引用可以指向/引用
子类的对象,我们称为切片
p1和p2是基类指针,它们调用的
函数恰好还被重写了,所以这里符合
多态,p1指针指向的内容是Person
所以它调用Person中的函数,然而p2
指针指向的内容是Student,所以它
调用的是Student中的函数!
依次打印:"买票-全家","买票-半价"
4. 构成多态的两个特例
特例一:
子类的虚函数不写virtual
依旧构成多态
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:void BuyTicket() { cout << "买票-半价" << endl; }
};
Person* p1 = new Person;
Person* p2 = new Student;
p1->BuyTicket();
p2->BuyTicket();
这样写也是构成多态的!
特例二:
基类与派生类虚函数返回值类型不同
也可以构成多态(返回值必须满足某种条件)
class A{};
class B : public A {};class Person {
public:virtual A* f() {return new A;}
};
class Student : public Person {
public:virtual B* f() {return new B;}
};
父类的返回值要返回父类
子类的返回值要返回子类
注意事项1:
父类不写virtual,而子类的同名
函数写了virtual,这是不构成多态的!
class Person {
public:void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
不构成多态!
注意事项2:
在继承体系中,父子类的同名
函数不构成重写就构成隐藏,不可能构成重载!
5. 多态的底层原理分析(一)
如果你单纯的认为Base类只有一个
整型变量占用空间的话,那你就上当啦!
事实上在32位机器下,这里的结果是8
在64位机器下,这里的结果是16!
这是因为它除了有一个变量外,还有
一个指针,此指针指向一个虚函数表
我们通过以下的代码来观察内存:
class A
{
public:virtual void func1(){cout << "父类func1";}
private:int _a;
};
class B : public A
{
public:virtual void func1(){cout << "子类func1";}
private:int _b;
};int main()
{A a;B b;return 0;
}
此指针叫虚表指针:vfptr,也就是
virtual function ptr
这个指针并不是直接指向虚函数的地址
而是指向一个虚函数表,可以理解位一个
数组,此数组中存放着此对象中所有的虚
函数的地址,它们的关系可以用下图表示:
注:不管有没有继承体系或多态
只要有虚函数就有虚表!
6. 多态底层原理分析(二)
现在得出一个结论:有虚函数的
类对象中还存放了一个虚表指针!
那么父类和子类的虚表指针和指向
的内容有什么不同或相同处吗?
形成多态现象的原理又是什么?
我来一一解答这些问题:
通过下面的代码来观察内存情况
得出父子类虚表的关联:
class A
{
public:virtual void func1()cout << "父类func1";virtual void func2()cout << "父类func2";
private:int _a;
};
class B : public A
{
public:virtual void func1()cout << "子类func1";
private:int _b;
};
int main()
{A a;B b;return 0;
}
请看下图观察情况:
结论:
父类和子类的虚表指针是不同的
证明父子类各有一张虚函数表!
函数func1在子类中被重写了,所以
父子类虚表中的func1函数地址是不同的
函数func2没有被子类重写,所以
父子类虚表中的func2函数地址是相同的
拓展结论:同一个类的不同对象共用一个虚表
多态的原理深度剖析:
当一个函数A被重写时,它的父类虚表存放
父类函数A的地址,子类虚表存放的是子类
函数A的地址!
当父类的指针或引用指向子类空间时
调用虚函数时,会到指向对象的虚表中
中找到对应的虚函数地址,进行调用!
拓展结论:
父子类都只有A函数或无函数时
-
若父类写了虚函数A,而子类
甚至没有写函数A,此时子类对象中
存储的虚函数地址与父类相同 -
若父类甚至没有写函数A,而子类
直接写了虚函数A,则父类对象中没有
虚表,而子类对象中有虚表(存放A)
7. 多态中的两个关键字
final:
修饰虚函数,表示该虚函数不能被重写
override:
检查子类类虚函数是否重写了
基类虚函数如果没有重写编译报错
8. 抽象类以及虚函数的几个结论
抽象类概念:
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写
抽象类的只需了解概念,实际中
使用到的场景很少
关于虚函数的几个小结论:
- 析构函数最好定义为虚函数
- 构造函数不能定义为虚函数
- 静态成员函数不能是虚函数
- 内联函数(inline)不能是虚函数
为什么说析构函数最好定义为虚函数?
请看下面的例子:
class Person {
public:virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数
//下面的delete对象调用析构函数,才能构成多态
//才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}
若析构函数不是虚函数,delete ptr2时
不符合多态,ptr2是Person类型指针
就只会调用Person类的析构,会有问题
若析构函数是虚函数,delete ptr2时
构成多态的条件,指针指向父类的对象
就调用父类的析构,指向子类的对象
就调用子类的析构,这样才是正确的!
9. 总结以及拓展
多态在校招的笔试面试中考察的
非常之多,很多面试官都喜欢在这
上面考察学生的掌握C++语法的程度
所以同学们请耐心学习!
拓展阅读:
多继承场景下的多态
相关文章:

【C++进阶(九)】C++多态深度剖析
💓博主CSDN主页:杭电码农-NEO💓 ⏩专栏分类:C从入门到精通⏪ 🚚代码仓库:NEO的学习日记🚚 🌹关注我🫵带你学习C 🔝🔝 多态 1. 前言2. 多态的概念以及定义3. 多态的实…...
第二节——Vue 基本介绍
一、MV*的理解 1、概念 在计算机编程领域,MV*(也称为MVC、MVP、MVVM等)是一种用于组织和设计应用程序结构的模式。这些模式旨在实现应用程序的解耦、可维护性和可扩展性。MV代表着Model-View-(表示控制器或视图模型等其他组件&a…...

基于ResNet34的花朵分类
一.数据集准备 新建一个项目文件夹ResNet,并在里面建立data_set文件夹用来保存数据集,在data_set文件夹下创建新文件夹"flower_data",点击链接下载花分类数据集https://storage.googleapis.com/download.tensorflow.org/example_i…...
[计算机提升] 数据及相关概念
1.9 数据及相关概念 1.9.1 数据、信息 在Windows系统中,数据是指事实或信息的集合,可以是数字、文本、图像、声音等形式的内容。数据是计算机系统中处理和操作的基本元素,是信息的表现形式和载体。 与信息相比,数据的范围更广泛…...
第18章 SpringCloud生态(二)
18.11 说说你了解的负载均衡算法 难度:★★ 重点:★★★★ 白话解析 常用的负载均衡算法有: 1、轮询(Round Robin):说白了就是让服务器排好队,一个个轮着来调用;Ribbon默认采用该算法。 优点:实现起来简单; 缺点:服务器性能不一样的情况下,导致能力强的会经常空闲…...
【Android】BRVAH多布局实现
前言 基于3.0.4版本的BRVAH框架实现的 实现方法 1.创建多个不同类型的布局(步骤忽略) 2.创建数据实体类 数据类要实现【MultiItemEntity】接口 class MyMultiItemEntity(//获取布局类型override var itemType: Int,var tractorRes: Int? null,va…...
AWS SAP-C02教程9-节省成本
SAP-C01变成SAP-C02的时候,最大的变化就是没有把成本单独列出一个模块,但是成本依然包含在各个其它模块之中,所以成本还是很重要的。本章将列举一些成本优化方案以及一些成本辅助功能。 目录 1 Cost Allocation Tags2 Trusted Advisor2.1 AWS Support Plans2.2 基本特性2.3…...

[CSP-S 2023] 种树 —— 二分+前缀和
This way 题意: 一开始以为是水题,敲了一个二分贪心检查的代码,20分。发现从根往某个节点x走的时候,一路走来的子树上的节点到已栽树的节点的距离会变短,那么并不能按照初始情况贪心。 于是就想着检查时候用线段树…...
【LeetCode周赛】LeetCode第368场周赛
目录 元素和最小的山形三元组 I元素和最小的山形三元组 II合法分组的最少组数 元素和最小的山形三元组 I 给你一个下标从 0 开始的整数数组 nums 。 如果下标三元组 (i, j, k) 满足下述全部条件,则认为它是一个山形三元组 : i < j < k nums[i] &l…...

【智慧工地源码】基于AI视觉技术赋能智慧工地
伴随着技术的不断发展,信息化手段、移动技术、智能穿戴及工具在工程施工阶段的应用不断提升,智慧工地概念应运而生,庞大的建设规模催生着智慧工地的探索和研发。 建筑施工具有周期长、环境复杂、工序繁杂、人员流动性大等特点,所以…...

云服务器搭建Hadoop分布式
文章目录 1.服务器配置2.Java环境3. 安装Hadoop4. 集群配置5. 编写集群的启动脚本 1.服务器配置 服务器主机名配置115.157.197.82s110核115.157.197.84s210核115.157.197.109s310核115.157.197.31s410核115.157.197.60gracal10核 所有的软件安装在/opt/module下,软…...
2678. 老人的数目
给你一个下标从 0 开始的字符串 details 。details 中每个元素都是一位乘客的信息,信息用长度为 15 的字符串表示,表示方式如下: 前十个字符是乘客的手机号码。 接下来的一个字符是乘客的性别。 接下来两个字符是乘客的年龄。 最后两个字符是…...

【刷题-牛客】出栈、入栈的顺序匹配 (代码+动态演示)
【刷题-牛客】出栈、入栈的顺序匹配 (代码动态演示) 文章目录 【刷题-牛客】出栈、入栈的顺序匹配 (代码动态演示) 解题思路 动图演示完整代码多组测试 💗题目描述 💗: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个…...

vscode类似GitHub Copilot的插件推荐
由于GitHub Copilot前段时间学生认证的账号掉了很多,某宝激活也是价格翻了几倍,而却,拿来用一天就掉线,可以试试同类免费的插件哦。 例如:TabNine,下载插件后,他会提示你登录,直接登…...

Html -- 文字时钟
Html – 文字时钟 文字时钟,之前在Android上实现了相关效果,闲来无事,弄个网页版的玩玩。。。直接上代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><titl…...
快问快答:关于线上流量卡“归属地随机”几个问题!
在网上办过流量卡的朋友应该都知道,资费虽然便宜,但是归属地却是异地,今天小编就给大家聊一聊关于流量卡归属地的问题。 网上的流量卡都是归属地随机的卡,今天小编以问答的方式给大家普及一下,如果对于归属地有疑问…...

Linux常用命令——clock命令
在线Linux命令查询工具 clock 用于调整 RTC 时间。 补充说明 clock命令用于调整 RTC 时间。 RTC 是电脑内建的硬件时间,执行这项指令可以显示现在时刻,调整硬件时钟的时间,将系统时间设成与硬件时钟之时间一致,或是把系统时间…...

澎湃OS上线:小米告别MIUI,跟小米汽车Say Hi
作者 | Amy 编辑 | 德新 10月17日,雷军发博官宣,「小米将启用全新操作系统,小米澎湃OS(Xiaomi HyperOS)」。 短短几百字的微博,数次提到了「小米汽车」: 小米向人车家全生态迈进,…...
域名不部署SSL证书有什么影响?
SSL证书是保护网站数据传输安全的重要工具,通过加密用户和服务器之间的通信来确保数据的保密性和完整性。然而,如果一个域名没有部署SSL证书,会对网站和用户产生一系列的负面影响。下文中将介绍域名不部署SSL证书的影响,并提供相应…...

Delphi 编程实现拖动排序并输出到文档
介绍:实现拖动排序功能,并将排序后的内容输出到文档中。我们将使用 Delphi 的组件来创建一个界面,其中包括一个 Memo 控件用于输入内容,一个 ListBox 控件用于显示排序后的内容,并且提供按钮来触发排序和输出操作。 代…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...