【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 控件用于显示排序后的内容,并且提供按钮来触发排序和输出操作。 代…...
【2026奇点大会独家解码】:AIAgent视觉导航的5大技术断层与3个月落地实战路径
第一章:2026奇点大会AIAgent视觉导航技术全景图谱 2026奇点智能技术大会(https://ml-summit.org) 2026奇点大会首次将AIAgent视觉导航确立为跨模态具身智能的核心使能技术,聚焦于动态场景理解、多尺度空间表征与实时闭环决策的协同演进。本届大会展示的…...
软件可维护性的修改扩展与理解难度
软件可维护性的修改扩展与理解难度 在软件开发的生命周期中,可维护性是衡量软件质量的重要指标之一。随着业务需求的不断变化和技术的迭代更新,软件需要频繁修改和扩展,而代码的可维护性直接影响开发团队的工作效率。理解难度则是可维护性的…...
大众点评全站数据采集终极指南:破解动态字体加密的完整爬虫方案
大众点评全站数据采集终极指南:破解动态字体加密的完整爬虫方案 【免费下载链接】dianping_spider 大众点评爬虫(全站可爬,解决动态字体加密,非OCR)。持续更新 项目地址: https://gitcode.com/gh_mirrors/di/dianpin…...
Qwen3-14B行业落地案例:金融研报摘要、医疗问诊辅助、客服话术生成
Qwen3-14B行业落地案例:金融研报摘要、医疗问诊辅助、客服话术生成 1. 开篇:私有部署镜像的价值 Qwen3-14B私有部署镜像为行业应用提供了强大的技术支持。这个经过优化的镜像版本完美适配RTX 4090D 24GB显存配置,内置完整运行环境与模型依赖…...
告别轮询!用C++和ADS Notification模式实时监听倍福PLC变量变化(附完整代码)
工业级实时数据监听:C与倍福ADS Notification深度实践 在工业自动化领域,数据采集的实时性往往直接关系到生产效率和系统稳定性。传统轮询方式不仅占用大量网络带宽,还可能导致关键状态变化的延迟捕获。以汽车焊装车间为例,当机器…...
代码签名证书怎么申请与选择?
在数字化浪潮席卷全球的今天,软件安全已成为企业与开发者不可回避的核心议题。恶意代码篡改、软件伪造等威胁层出不穷,而代码签名证书正是为软件安全筑起的第一道防线。它通过数字签名技术为软件赋予唯一的身份标识,确保代码在传输与安装过程…...
告别PS!用SAM 3镜像快速分割图片视频中的物体,效果实测惊艳
告别PS!用SAM 3镜像快速分割图片视频中的物体,效果实测惊艳 1. 引言:为什么你需要SAM 3? 想象一下这样的场景:你正在编辑一段产品展示视频,需要把背景中的路人全部去掉;或者你有一张复杂的风景…...
Rust的匹配中的常量折叠
Rust的匹配中的常量折叠:高效模式匹配的幕后功臣 Rust以其出色的性能和安全性闻名,而模式匹配(match)是其核心特性之一。在编译阶段,Rust通过常量折叠(Constant Folding)优化匹配逻辑ÿ…...
从零适配OV5640:为i.MX6ULL定制1024x600分辨率与30FPS帧率
1. OV5640与i.MX6ULL的硬件适配基础 在嵌入式视觉系统中,摄像头与处理器的搭配就像咖啡与咖啡机的组合——需要完美匹配才能产出理想效果。OV5640这颗500万像素的传感器与i.MX6ULL处理器的联姻,首先要解决的就是物理层面的"对话协议"问题。 硬…...
Qwen3-0.6B-FP8部署教程:vLLM服务健康检查(llm.log)、Chainlit端口映射与CORS配置
Qwen3-0.6B-FP8部署教程:vLLM服务健康检查、Chainlit端口映射与CORS配置 1. 开篇:为什么你需要这篇教程? 如果你正在尝试部署一个轻量级的AI模型,比如Qwen3-0.6B-FP8,并且希望它能稳定运行,还能通过一个漂…...
