C++中虚函数表的概念
当一个类对象指针调用虚函数时,这就涉及到 运行时多态 的概念。这意味着实际调用的函数取决于对象的实际类型,而不仅仅是指针的静态类型。
假设我们有以下的类层次结构:
class Base {
public:virtual void print() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:void print() override {std::cout << "Derived class" << std::endl;}
};
创建对象: 首先,我们创建一个对象。可以是基类类型的对象,也可以是派生类类型的对象。例如:
Base baseObj;
Derived derivedObj;
创建指针: 然后,我们可以创建指向这些对象的指针,使用基类指针来指向派生类对象。例如:
Copy code
Base* ptrToBase = &baseObj;
Base* ptrToDerived = &derivedObj;
虚函数表:对于包含虚函数的类,每个对象的内存中通常会包含一个 指向虚函数表的指针。虚函数表是一个包含虚函数指针的数组,其中的每个指针指向对应虚函数的实际代码地址。
以下是一个示例:
int main() {Base baseObj;Derived derivedObj;Base* ptrToBase = &baseObj;Base* ptrToDerived = &derivedObj;ptrToBase->print(); // 输出 "Base class"ptrToDerived->print(); // 输出 "Derived class"return 0;
}
在这个示例中,当通过基类指针调用虚函数时,实际上调用的是对象的实际类型所对应的虚函数。这就是运行时多态性的表现。
虚函数表是针对每个类生成的(每个类都有一个),并且每个类的对象实例都会有一个指向其对应类的虚函数表的指针。虚函数表本身是一个指针数组,其中存储着该类的所有虚函数的指针。每个虚函数指针指向实际的虚函数代码。虚函数表是个数组,元素数量等于该类中声明的虚函数的数量。
在上面这个示例中:
- 对于 Base 类,它只有一个虚函数 print,因此其虚函数表只有一个指针,指向 Base::print 函数。
- 对于 Derived 类,它重写了 print 函数,因此其虚函数表也只有一个指针,指向 Derived::print 函数。
当使用对象指针或引用调用虚函数时,整个过程可以分为编译期和运行期两个阶段。以下是详细的虚函数调用过程:
编译期(Compile Time):
- 编译器识别调用: 编译器在编译期根据对象指针或引用的静态类型(即声明时的类型)来识别将要调用的虚函数。
- 查找虚函数表: 编译器通过对象指针的静态类型找到相应类的虚函数表,然后根据虚函数的位置(通常是函数在虚函数表中的索引)确定要调用的虚函数的地址。
- 生成调用指令: 编译器生成机器代码,将虚函数调用指令指向静态确定的虚函数地址。这个地址是根据对象指针的静态类型在编译期计算出来的。
运行期(Run Time):
- 实际对象确定: 在程序运行时,通过对象指针或引用调用虚函数。这时,程序运行期间实际的对象类型才会被确定。
- 查找虚函数表(vptr): 当调用虚函数时,程序使用对象指针中存储的虚函数指针(vptr)来查找虚函数表的地址。
- 动态修正地址: 从虚函数表中根据编译期确定的虚函数位置找到实际要调用的虚函数的地址。这个过程是在运行期根据实际对象类型进行的。
- 调用虚函数: 最终,调用虚函数的指令将指向运行期确定的虚函数地址,从而调用正确的虚函数。
大家可能跟我有相同异或:既然编译器可以知道这一行是调用的虚函数,那就应该知道编译期间不太能确定实际上的函数调用地址,为什么还要去解析一遍函数地址,动态运行期再去修正这个地址?
-
静态绑定和虚函数表的优化: 虽然编译器在编译期可以知道函数是否是虚函数,但它也要考虑静态绑定的情况。如果编译器在编译期确定某个函数是虚函数,但在特定的调用点,它知道调用的函数就是该类中的那个实现,编译器可以进行静态绑定优化,避免虚函数表的查找。比如:
Base* ptr = new Base(); ptr->print()虽然调用虚函数,但是很明显编译期间就能确定正确的地址,从而可以进行优化,省略动态绑定过程 -
编译器优化和内联: 编译器在编译期根据静态类型就能够确定调用的函数,这样它可以进行更多的优化。如果函数是非虚的,编译器可以尝试内联函数调用,减少函数调用的开销。
-
错误检查和类型安全: 静态类型在编译期可以帮助编译器检查代码中的错误。如果某个类没有实现特定的虚函数,编译器可以在编译期就发现这个错误,而不是等到运行时。
-
虚函数的重载解析: 在 C++ 中,虚函数可以被重载。编译器在编译期需要知道调用哪个函数的版本,以便正确生成调用代码。
尽管在编译期可以确定虚函数的一些信息,但在运行时,由于多态性的需要,最终的调用地址还是要根据实际对象的类型进行动态确定,以实现正确的多态行为。编译期的信息对于优化、错误检查和静态绑定等方面仍然有重要作用。
最后。还可以看一下这个文章,有图解的
相关文章:
C++中虚函数表的概念
当一个类对象指针调用虚函数时,这就涉及到 运行时多态 的概念。这意味着实际调用的函数取决于对象的实际类型,而不仅仅是指针的静态类型。 假设我们有以下的类层次结构: class Base { public:virtual void print() {std::cout << &qu…...
代码随想录算法训练营第四十八天 | 198.打家劫舍,213.打家劫舍II,337.打家劫舍III
代码随想录算法训练营第四十八天 | 198.打家劫舍,213.打家劫舍II,337.打家劫舍III 198.打家劫舍213.打家劫舍II337.打家劫舍III 198.打家劫舍 题目链接 视频讲解 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金ÿ…...
uniapp项目实战系列(1):导入数据库,启动后端服务,开启代码托管
目录 前言前期准备1.数据库的导入2.运行后端服务2.1数据库的后端配置2.2后端服务下载依赖,第三方库2.3启动后端服务 3.开启gitcode代码托管 ✨ 原创不易,还希望各位大佬支持一下! 👍 点赞,你的认可是我创作的动力&…...
在互联网+的背景下,企业如何创新客户服务?
随着互联网的发展,开始数字化转型的潮流,移动互联网平台为各个行业带来了发展的新方向。企业有了移动互联网的加持,为客户提供了更好的服务。当移动互联网平台能够为客户提供更好的用户体验时,相应地,客户也给企业带来…...
国内的化妆品核辐射检测
化妆品核辐射物质检测是指检测化妆品中的放射性物质,包括放射性核素和放射性同位素。这些放射性物质主要来源于环境中的放射性污染,如空气、水和土壤中的放射性物质,以及化妆品生产过程中的放射性污染,如原料、设备、工艺等。化妆…...
春秋云镜:CVE-2019-9042(Sitemagic CMS v4.4 任意文件上传漏洞)
一、题目 靶标介绍: Sitemagic CMS v4.4 index.php?SMExtSMFiles 存在任意文件上传漏洞,攻击者可上传恶意代码执行系统命令。 进入题目: admin/admin /index.php?SMExtSMFiles&SMTemplateTypeBasic&SMExecModeDedicated&SMFil…...
20230828工作日志:
今天遇到了很多问题,下次可以做得更好更快的几个地方: 1 sql语句的检查 肯定要先在navicate 里执行看,是否有语法错误。即使没有,也还是要注意一些问题:IDEA里换行的时候,“后面要空一格,如果连…...
flink on yarn 部署
需要jars -rwxr-xrwx 3 root supergroup 58284 2022-11-30 03:44 /lib/flink/commons-cli-1.5.0.jar -rw-r--r-- 3 root supergroup 48497 2022-12-10 03:04 /lib/flink/flink-cep-scala_2.12-1.14.3.jar -rw-r--r-- 3 root supergroup 189468 2022-12-10…...
postgresql基于postgis常用空间函数
1、ST_AsGeoJSON 图元转geojson格式 select ST_AsGeoJSON(l.geom) from g_zd l limit 10 2、 ST_Transform 坐标转换 select st_transform(l.shape, 3857) from sde_wf_cyyq l limit 10select st_astext(st_transform(l.shape, 3857)) from sde_wf_cyyq l limit 103、st_aste…...
详细讲解移植u-boot.2022.10版本移植到开发板基本方法
大家好,我是ST。 今天给大家讲一讲如何将u-boot.2022.10版本移植到imx6ull开发板上。 环境 选项内容编译主机UbuntuLTS 18.04目标板ATK I.MX6ULL(512MB DDR3 8GB EMMC)u-boot版本2022.10交叉编译工具链gcc-linaro-7.5.0-2019.12-i686…...
Vue.js2+Cesium1.103.0 十一、Three.js 炸裂效果
Vue.js2Cesium1.103.0 十一、Three.js 炸裂效果 Demo ThreeModelBoom.vue <template><div:id"id"class"three_container"/> </template><script> /* eslint-disable eqeqeq */ /* eslint-disable no-unused-vars */ /* eslint-d…...
Nodejs快速搭建简单的HTTP服务器,并发布公网远程访问
前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation(原为 Node.js Foundation,已与 JS Foundation 合并)持有和维护,亦为 Linux 基金会的项目。Node.js 采用 Google 开发…...
爬虫入门01
1. 请求头中最常见的一些重要内容 User-Agent : 请求载体的身份标识(⽤啥发送的请求)Referer: 防盗链(这次请求是从哪个⻚⾯来的? 反爬会⽤到)cookie: 本地字符串数据信息(⽤户登录信息, 反爬的token) 2. 响应头中一些重要内容 cookie: 本地字符串数据信息(⽤户登录信息, 反…...
解读GIS软件:从ArcGIS到山海鲸可视化的全方位介绍
在现代社会,地理信息系统(GIS)的应用已经渗透到了各个领域,为我们提供了丰富的地理数据分析和可视化工具。下面介绍几款常见的GIS工具软件,一起来了解它们的特点和优势。 1. ArcGIS: ArcGIS由Esri公司开发,…...
嵌入式通用硬件模块设计——串口音频播放模块
模块功能展示: 串口音频控制模块 一、简介 方案为串口音频播放芯片功放芯片,口音频播放芯片IC为my1690-16s,功放为PAM8406。 1、my1690-16s 迈优科技的一款由串口控制的插卡MP3播放控制芯片,支持串口控制播放指定音频、音量调节…...
【PLSQL】PLSQL基础
文章目录 一:记录类型1.语法2.代码实例 二:字符转换三:%TYPE和%ROWTYPE1.%TYPE2.%ROWTYPE 四:循环1.LOOP2.WHILE(推荐)3.数字式循环 五:游标1.游标定义及读取2.游标属性3.NO_DATA_FOUND和%NOTFO…...
【C++笔记】C++内存管理
【C笔记】C内存管理 一、C中动态内存申请的方式二、new和delete的实现原理2.1、operator new和operator delete函数 一、C中动态内存申请的方式 在C语言中我们需要动态申请空间的时候我们通常都是用malloc函数,但是malloc函数对自定义类型是没什么问题的࿰…...
十四五双碳双控时代下的“低碳认证”
目录 前言 十四五双碳双控时代下的“低碳认证” 一、关于“低碳认证” 二、低碳认证优势 三、环境产品认证EPD 四、EPD相关运营机构 五、碳中和相关机构 六、EPD的认证流程 七、低碳产品认证认证流程和要求 八、相关机构认证证书样例 九、证书附件表 前言 通过本篇文…...
Android——基本控件(下)(十九)
1. 菜单:Menu 1.1 知识点 (1)掌握Android中菜单的使用; (2)掌握选项菜单(OptionsMenu)的使用; (3)掌握上下文菜单(ContextMenu&am…...
聚类分析 | MATLAB实现基于DBSCAD密度聚类算法可视化
聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化 目录 聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于DBSCAD密度聚类算法可视化,MATLAB程序。 使用带有KD树加速的dbscan_with_kdtree函数进行…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
