【C++】多态原理剖析
目录
1.虚表指针与虚表
2.多态原理剖析
1.虚表指针与虚表
🍪类的大小计算规则
- 一个类的大小,实际就是该类中成员变量之和,需要注意内存对齐
- 空类:编译器给空类一个字节来唯一标识这个类的对象
对于下面的Base类,它的大小应该是类中成员变量之和,一个int成员,一个char成员,根据结构体内存对齐规则,sizeof(Base) = 8byte
class Base
{
public:virtual void func1(){cout << "func1()" << endl;}
private:int _b = 1;char _ch;
};
成员函数为虚函数

成员函数是普通函数

但是代码的运行结果为sizeof(Base) = 12byte,与上面的分析结果不一致
Base类中的成员函数使用virtual修饰,可以推断包含虚函数的类大小计算规则和包含普通函数的类大小计算规则可能存在差异
我们创建一个Base对象,进行进一步分析

从监视窗口,可以发现,实例化的Base对象成员构成,除了两个成员变量外,还有一个数组指针,而数组成员又是指针类型,所以准确来说,_vfptr是一个指针数组指针
🍪虚表指针和虚表
虚表指针:对象中的这个指针叫做虚函数表指针(v--virtual,f--function)。
虚表:一个含有虚函数的类中至少有一个虚函数表指针,因为虚函数的地址要被放到虚函数表(虚表)中
📖Note:
- 虚函数表本质是一个存虚函数指针的指针数组,一般这个数组最后面放了一个nullptr
虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是虚函数的指针存在虚表中,可以通过这个指针找到虚函数
实例化对象中存的不是虚表,存的是虚表指针,通过这个指针可以找到虚表。

接下来执行三个操作
- Base增加一个虚函数func2和一个普通函数fun3。
- 增加一个派生类Derive去继承Base
- Derive中重写func1
class Base
{
public:virtual void func1(){cout << "func1()" << endl;}virtual void func2(){cout << "func2()" << endl;}// 普通函数void func3(){cout << "func3()" << endl;}
private:int _b = 1;char _ch;
};class Derive : public Base
{
public:virtual void func1(){cout << "Derive::func1()" << endl;}
private:int _d = 2;
};


从监视窗口可以发现
1️⃣基类的虚表指针值_vfptr != 派生类的虚表指针值_vfptr
2️⃣基类的普通函数func3不会存入虚表之中,继承之后也不会存入派生类的虚表
3️⃣派生类中对func1完成了重写,d的虚表中存的是重写的Derive::func1,所以虚函数的重写(语法层)也叫作覆盖(原理层),覆盖就是指虚表中虚函数的覆盖。
🍪派生类的虚表指针总结:
- 派生类对象中也有一个虚表指针,派生类继承的成员包括虚表指针,但需要注意基类和派生类的虚表不是同一份
- 基类中的虚函数,派生类继承之后放进了虚表,基类中的普通函数,派生类继承之后不会放进虚表
派生类自己新增的虚函数按其在派生类中的声明次序增加到派生类虚表的最后,如下图所示


派生类的虚表生成过程:

2.多态原理剖析
基于上面创建的基类Base和派生类Derive,执行以下代码,观察执行结果

🍪分析:
- func3是基类Base中的定义的普通函数
- func1是基类Base中定义的虚函数,且派生类完成了对func1的重写,构成多态
🍪普通函数的调用,只与调用函数的对象的类型有关
前两次对func3的调用都是Base*类型的指针进行调用

🍪多态调用,与函数调用者指向的整个对象有关
- 第一次对func1的调用:ptr指向的是一个Base对象,对虚函数func1的调用需要到_vfptr指向的虚表中查找func1函数的地址进行调用,最终调用的是Base类中的函数func1
- 第二次对func1的调用:ptr指向的是Derive对象中Base的切片,对虚函数func1的调用仍然需要到_vfptr指向的虚表中查找func1函数的地址进行调用,但是这一次,_vfptr指向的虚表中,func1函数的地址已经更改成了Derive类中重写的func1函数地址,所以最终调用的是Derive中重写的func1函数

相关文章:
【C++】多态原理剖析
目录 1.虚表指针与虚表 2.多态原理剖析 1.虚表指针与虚表 🍪类的大小计算规则 一个类的大小,实际就是该类中成员变量之和,需要注意内存对齐空类:编译器给空类一个字节来唯一标识这个类的对象 对于下面的Base类,它的…...
【Rust自学】20.4. 结语:Rust学习一阶段完成+附录
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 20.4.1. 总结 Rust初级学习之旅终于完成了!恭喜! 包括这篇文章,我们使用了110篇文章来学习Rust。 真…...
pytorch引用halcon写数据集
****加粗样式虽然啰嗦一点,但好歹halcon自己熟悉,不会忘记,用os 和 pil会导致脑子记得东西太多 import halcon as ha import torch from torch.utils.data import Datasetpath0 rE:\BaiduNetdiskDownload\cell class MyDataset(Dataset):de…...
让文物“活”起来,以3D数字化技术传承文物历史文化!
文物,作为不可再生的宝贵资源,其任何毁损都是无法逆转的损失。然而,当前文物保护与修复领域仍大量依赖传统技术,同时,文物管理机构和专业团队的力量相对薄弱,亟需引入数字化管理手段以应对挑战。 积木易搭…...
aarch64 Ubuntu20.04 安装docker
安装 docker 依赖项:sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release添加 Docker GPG 密钥:curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyr…...
JAVA:CloseableHttpClient 进行 HTTP 请求的技术指南
1、简述 CloseableHttpClient 是 Apache HttpComponents 提供的一个强大 HTTP 客户端库。它允许 Java 程序与 HTTP/HTTPS 服务交互,可以发送 GET、POST 等各种请求类型,并处理响应。该库广泛用于 REST API 调用、文件上传和下载等场景。 2、特性 Close…...
Mac上搭建k8s环境——Minikube
1、在mac上安装Minikube可执行程序 brew cask install minikub 安装后使用minikube version命令查看版本 2、安装docker环境 brew install --cask --appdir/Applications docker #安装docker open -a Docker #启动docker 3、安装kubectl curl -LO https://storage.g…...
经典排序算法复习----C语言
经典排序算法复习 分类 交换类 冒泡快排 分配类 计数排序基数排序 选择类 选择排序 堆排序 归并类 归并排序 插入类 直接插入排序 希尔排序 折半插入排序 冒泡排序 基于交换。每一轮找最大值放到数组尾部 //冒泡排序 void bubSort(int* arr,int size){bool sorte…...
自动驾驶数据集三剑客:nuScenes、nuImages 与 nuPlan 的技术矩阵与生态协同
目录 1、引言 2、主要内容 2.1、定位对比:感知与规划的全维覆盖 2.2、数据与技术特性对比 2.3、技术协同:构建全栈研发生态 2.4、应用场景与评估体系 2.5、总结与展望 3、参考文献 1、引言 随着自动驾驶技术向全栈化迈进,Motional 团…...
[LUA ERROR] bad light userdata pointer
Cocos2d项目,targetSdkVersion30,在 android 13 设备运行报错: [LUA ERROR] bad light userdata pointer ,导致黑屏。 参考 cocos2dx 适配64位 arm64-v8a 30 lua 提示 bad light userdata pointer 黑屏-CSDN博客的方法 下载最新的Cocos2dx …...
【Java八股】JVM
JVM 1. jvm内存区域分为哪些部分 线程私有的:程序计数器、虚拟机栈、本地方法栈 程序计数器:指示当前线程执行到的字节码文件的行号,是线程切换后保证线程能恢复到正确的执行位置的关键 虚拟机栈:用于存储方法调用的数据&…...
集成学习(一):从理论到实战(附代码)
一、引言 在机器学习领域,打造一个独立、强大的算法是解决问题的关键。然而,集成学习提供了一种不同的视角:通过组合多个“弱”学习器来创建一个更强大的模型。本文探讨集成学习的思想、方法及其应用。 二、机器学习 vs 集成学习思想 传统…...
Netty:高性能网络应用框架的深度解析
引言 Netty 是由 JBoss 提供的一个开源的 Java NIO 客户端/服务器框架,它用以快速开发网络应用程序,如协议服务器和客户端。它的设计目标是提供异步事件驱动的网络应用程序框架,支持高效的网络通信和数据处理。Netty 在性能、可扩展性、安全…...
神经网络常见激活函数 3-ReLU函数(修正线性单元)
文章目录 ReLU函数求导函数和导函数图像优缺点pytorch 中的 ReLU 函数tensorflow 中的ReLU函数 ReLU 修正线性单元 (Rectified Linear Unit) 函数求导 ReLU函数 ReLU max ( 0 , x ) { x x ≥ 0 0 x < 0 \begin{aligned} \operatorname{ReL…...
Android开发获取缓存,删除缓存
Android开发获取缓存,删除缓存 app设置中往往有清理缓存的功能。会显示当前缓存时多少,然后可以点击清理缓存 直接上代码: object CacheHelper {/*** 获取缓存大小* param context* return* throws Exception*/JvmStaticfun getTotalCache…...
如何通过PHP接入DeepSeek的API
想知道如何通过PHP接入DeepSeek的API。看起来他对之前的Python步骤比较熟悉,但这次想用PHP实现。 首先,我需要回顾一下DeepSeek API的文档,确认它支持哪些方法和参数。假设用户已经配置了环境变量,比如API密钥,接下来…...
一种基于Leaflet.Legend的图例动态更新方法
目录 前言 一、场景再现 1、需求描述 2、核心方法介绍 3、存在的问题 二、问题解决 1、重复解决办法 2、图例不展示解决办法 3、成果展示 三、总结 前言 在当今数字化时代,地理信息系统(GIS)技术已经广泛应用于各个领域,…...
Spring Boot: 使用 @Transactional 和 TransactionSynchronization 在事务提交后发送消息到 MQ
Spring Boot: 使用 Transactional 和 TransactionSynchronization 在事务提交后发送消息到 MQ 在微服务架构中,确保消息的可靠性和一致性非常重要,尤其是在涉及到分布式事务的场景中。本文将演示如何使用 Spring Boot 的事务机制和 TransactionSynchron…...
LQB(2)-python-枚举
前言 python中的枚举一般有两个说法,一个是枚举算法(暴力求解法,算法层面),一个是遍历使用enumerate()函数或者enum模块创建()。 暴力求解法在之前的博文里面讲过了👇,…...
MongoDB开发规范
分级名称定义P0核心系统需7*24不间断运行,一旦发生不可用,会直接影响核心业务的连续性,或影响公司名誉、品牌、集团战略、营销计划等,可能会造成P0-P2级事故发生。P1次核心系统这些系统降级或不可用,会间接影响用户使用…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001
qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类,直接把源文件拖进VS的项目里,然后VS卡住十秒,然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分,导致编译的时候找不到了。因…...
Java中栈的多种实现类详解
Java中栈的多种实现类详解:Stack、LinkedList与ArrayDeque全方位对比 前言一、Stack类——Java最早的栈实现1.1 Stack类简介1.2 常用方法1.3 优缺点分析 二、LinkedList类——灵活的双端链表2.1 LinkedList类简介2.2 常用方法2.3 优缺点分析 三、ArrayDeque类——高…...
Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...
生信服务器 | 做生信为什么推荐使用Linux服务器?
原文链接:生信服务器 | 做生信为什么推荐使用Linux服务器? 一、 做生信为什么推荐使用服务器? 大家好,我是小杜。在做生信分析的同学,或是将接触学习生信分析的同学,<font style"color:rgb(53, 1…...
AT模式下的全局锁冲突如何解决?
一、全局锁冲突解决方案 1. 业务层重试机制(推荐方案) Service public class OrderService {GlobalTransactionalRetryable(maxAttempts 3, backoff Backoff(delay 100))public void createOrder(OrderDTO order) {// 库存扣减(自动加全…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
