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函数进行…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
