【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 控件用于显示排序后的内容,并且提供按钮来触发排序和输出操作。 代…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...
Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...
