C++之虚函数、虚函数表
C++ 虚函数、虚函数表详解与实践
C++中虚函数是实现多态的重要技术,接下来将从汇编、以及gdb调试运行方面下手全面了解虚函数、虚函数表、以及虚函数调用。
原理初认识
- 一个由虚函数的类将会有一个虚函数表,且所有该类的实例化对象共享一个虚函数表。
- 虚函数表将存在代码的
.data.rel.ro段中(该段表示需要重定位的只读数据段),在代码(elf可执行文件)的.rela.dyn段中指出了需要重定位的条目。 - 每个有虚函数的类的实例化对象都有一个指向该类虚函数表的成员指针变量(编译器自动创建)。
- 虚函数的调用会根据对象中的虚函数表去找对应的虚函数地址,然后再去调用对应的虚函数。
- 虚函数表的初始化是在编译时期(编译器直接将对应的虚函数地址放入虚函数表中),实例化对象的指向虚函数表的指针初始化是在构造函数中,其中指向虚函数表的指针是实现动态多态的重要技术。
- 类的构造函数不能是虚函数(原因见下面2.1的分析)。
一探究竟
1. 测试源代码
#include <iostream>// 父类
class Base {
public:// 纯虚函数virtual void call() = 0;
};// 子类
class SON : public Base {
public:// 重写父类纯虚函数void call() override {std::cout << "vir son call\n";}
};int main()
{Base* myson = new SON;// 调用重写的call函数myson->call();return 0;
}
2. 汇编代码分析
- 调用流程分析
main函数中会先调用SON的构造函数。SON的构造函数中会调用父类Base的构造函数,然后返回SON的构造函数。SON构造函数中会进行虚函数表指针的初始化(将SON类的虚函数表首地址放入该对象的虚函数表指针中)。SON构造函数完成剩下其他初始化就返回到main函数中。
-
由于是先调用父类的构造函数再初始化虚函数表指针,所以如果父类构造函数是虚函数的话,在子类对象虚函数表指针都还没初始化就去调用重写的虚构造函数显然是不行的(因为虚函数表指针都没初始化,怎么能调用到重写的虚构造函数)。
-
对应的汇编代码截屏

-
虚函数表所对应的
.data.rel.ro段,虚函数表地址为0x11d18,其中存放的重写的call函数地址为0xfd8.

-
SON类重写的call函数汇编代码截屏(地址分配为0xfd8)

- 从图二可以看出,在编译阶段编译器就已经将重写的
call地址放入了虚函数表中,所以可以知道了虚函数的初始化是在编译阶段。 - 从上面三张截屏可以看出来在
SON的构造函数中会将该类的虚函数地址放入该对象的虚函数表指针中,所以可以知道了对象的虚函数表指针初始化是在构造函数阶段。
- 从图二可以看出,在编译阶段编译器就已经将重写的
- 在
elf可执行程序执行时,看看虚函数表中对重写的虚函数地址的重定位。
-
.rela.dyn段的截屏,可以看出重定位表项记录了重写的call函数地址需要在加载时重新修改(重定位)

-
gdb调试时虚函数表中重写虚函数的实际地址截屏,可以看出确实被重定位了指向了重写的
call函数的真实地址

-
虚函数的调用

// 将实例化myson的this指针放入x0寄存器中 f38: f94017e0 ldr x0, [sp, #40]// 取出myson的第一个成员变量(即虚函数表指针) f3c: f9400000 ldr x0, [x0]// 访问虚函数表指针指向的空间(即虚函数表第一个表项)并将其放入x1寄存器中(此时x1寄存器中的值就是重写的call函数地址) f40: f9400001 ldr x1, [x0]// 将myson的this指针放入x0寄存器中 f44: f94017e0 ldr x0, [sp, #40]// 函数跳转至x1寄存器中存放的地址(即重写的call函数地址) f48: d63f0020 blr x1
总结
- 在此C++虚函数、虚函数表相关的知识点已经全部从实践的角度分析完。相关结论在1.原理初认识的时候就已经给出。
附录重要汇编代码
感兴趣的朋友可以细看
.rela.dyn段
Relocation section '.rela.dyn' at offset 0xa50 contains 20 entries:Offset Info Type Sym. Value Sym. Name + Addend
000000011cf0 000000000403 R_AARCH64_RELATIV f10
000000011cf8 000000000403 R_AARCH64_RELATIV fbc
000000011d00 000000000403 R_AARCH64_RELATIV ec0
000000011d10 000000000403 R_AARCH64_RELATIV 11d38
000000011d18 000000000403 R_AARCH64_RELATIV fd8
000000011d28 000000000403 R_AARCH64_RELATIV 11d50
000000011d40 000000000403 R_AARCH64_RELATIV 1088
000000011d48 000000000403 R_AARCH64_RELATIV 11d50
main函数
0000000000000f14 <main>:f14: a9bd7bfd stp x29, x30, [sp, #-48]!f18: 910003fd mov x29, spf1c: f9000bf3 str x19, [sp, #16]f20: d2800100 mov x0, #0x8 // #8f24: 97ffff93 bl d70 <_Znwm@plt>f28: aa0003f3 mov x19, x0f2c: aa1303e0 mov x0, x19f30: 9400003e bl 1028 <_ZN3SONC1Ev>f34: f90017f3 str x19, [sp, #40]f38: f94017e0 ldr x0, [sp, #40]f3c: f9400000 ldr x0, [x0]f40: f9400001 ldr x1, [x0]f44: f94017e0 ldr x0, [sp, #40]f48: d63f0020 blr x1f4c: 52800000 mov w0, #0x0 // #0f50: f9400bf3 ldr x19, [sp, #16]f54: a8c37bfd ldp x29, x30, [sp], #48f58: d65f03c0 ret
SON、Base类构造函数、重写的call函数
0000000000000fd8 <_ZN3SON4callEv>:fd8: a9be7bfd stp x29, x30, [sp, #-32]!fdc: 910003fd mov x29, spfe0: f9000fe0 str x0, [sp, #24]fe4: b0000000 adrp x0, 1000 <_ZN3SON4callEv+0x28>fe8: 9101e001 add x1, x0, #0x78fec: b0000080 adrp x0, 11000 <__FRAME_END__+0xfdbc>ff0: f947ec00 ldr x0, [x0, #4056]ff4: 97ffff5b bl d60 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>ff8: d503201f nopffc: a8c27bfd ldp x29, x30, [sp], #321000: d65f03c0 ret0000000000001004 <_ZN4BaseC1Ev>:1004: d10043ff sub sp, sp, #0x101008: f90007e0 str x0, [sp, #8]100c: 90000080 adrp x0, 11000 <__FRAME_END__+0xfdbc>1010: 9134c001 add x1, x0, #0xd301014: f94007e0 ldr x0, [sp, #8]1018: f9000001 str x1, [x0]101c: d503201f nop1020: 910043ff add sp, sp, #0x101024: d65f03c0 ret0000000000001028 <_ZN3SONC1Ev>:1028: a9be7bfd stp x29, x30, [sp, #-32]!102c: 910003fd mov x29, sp1030: f9000fe0 str x0, [sp, #24]1034: f9400fe0 ldr x0, [sp, #24]1038: 97fffff3 bl 1004 <_ZN4BaseC1Ev>103c: 90000080 adrp x0, 11000 <__FRAME_END__+0xfdbc>1040: 91346001 add x1, x0, #0xd181044: f9400fe0 ldr x0, [sp, #24]1048: f9000001 str x1, [x0]104c: d503201f nop1050: a8c27bfd ldp x29, x30, [sp], #321054: d65f03c0 ret
.data.rel.ro段,虚函数表
Disassembly of section .data.rel.ro:0000000000011d08 <_ZTV3SON>:...11d10: 00011d38 .inst 0x00011d38 ; undefined11d14: 00000000 udf #011d18: 00000fd8 udf #405611d1c: 00000000 udf #00000000000011d20 <_ZTV4Base>:...11d28: 00011d50 .inst 0x00011d50 ; undefined
相关文章:
C++之虚函数、虚函数表
C 虚函数、虚函数表详解与实践 C中虚函数是实现多态的重要技术,接下来将从汇编、以及gdb调试运行方面下手全面了解虚函数、虚函数表、以及虚函数调用。 原理初认识 一个由虚函数的类将会有一个虚函数表,且所有该类的实例化对象共享一个虚函数表。虚函…...
零基础学QT、C++(一)安装QT
目录 如何快速学习QT、C呢? 一、编译器、项目构建工具 1、编译器(介绍2款) 2、项目构建工具 二、安装QT 1、下载QT安装包 2、运行安装包 3、运行QT creator 4、导入开源项目 总结 闲谈 如何快速学习QT、C呢? 那就是项目驱动法&…...
Python SMTP 实现邮件发送功能
发送邮件的流程 登录我们邮箱, 书写接收者的邮箱, 书写题目与内容,添加附件, 点击发送。 邮件协议 smtp 是邮件发送的协议。pop3 是邮件接收的协议。 smtplib模块用法 创建协议对象:smtpObj smtplib.SMTP() 创建…...
低价窜货要如何管控
在竞争激烈的市场环境中,低价与窜货就像一对如影随形的“孪生兄弟”,给品牌的健康发展带来了极大的困扰。低价销售不仅压缩了合理的利润空间,破坏了市场的价格体系,还会引发恶性竞争,让整个市场陷入混乱无序的状态。而…...
如何看nginx.conf文件?
是的,你的理解是对的!在 Nginx 配置中,最内层的 location 确实是决定请求最终处理的“入口”。当请求进入 Nginx 时,Nginx 会根据请求的路径(即 URL)匹配 location 块,然后按照匹配的顺序逐层向…...
Linux中POSIX应用场景
Linux 提供了丰富的 POSIX(Portable Operating System Interface)标准接口,这些接口可以帮助开发者编写可移植、高效的应用程序。POSIX 标准定义了一系列系统调用和库函数,涵盖了文件操作、进程管理、线程管理、信号处理、同步机制…...
《动手学机器人学》笔记
目录 0.介绍1.概述|空间位置、姿态的描述(33)|《动手学机器人学》2.(2)-Robotics Toolbox①(V10.4)3.齐次坐标与变换矩阵4.一般形式的旋转变换矩阵5.(轴角法)…...
国产编辑器EverEdit - 文本编辑器的关键特性:文件变更实时监视,多头编辑不掉坑
1 监视文件变更 1.1 应用场景 某些时候,用户会使用多个编辑器打开同一个文件,如果在A编辑器修改保存,但是B编辑器没有重新打开,直接在B编辑器修改再保存,则可能造成在A编辑器中修改的内容丢失,因此&#x…...
化学蛋白质组学与药物靶点筛选:DARTS、LiP-MS、TPP、CETSA技术的深度解析
更多详情请看:LiP-MS药物靶点筛选技术 在药物研发的复杂过程中,药物靶点的筛选是关键环节之一。化学蛋白质组学技术的出现,为药物靶点筛选提供了强大的工具,化学蛋白质组学是一门研究细胞或组织中全部蛋白质的化学组成、结构、功…...
如何使用 Flutter DevTools 和 PerformanceOverlay 监控性能瓶颈
使用 Flutter DevTools 和 PerformanceOverlay 监控性能瓶颈:详细分析与实战 在开发 Flutter 应用时,性能问题可能会导致用户体验下降,比如页面卡顿、掉帧、内存泄漏等。为了定位和解决这些问题,Flutter 提供了强大的性能监控工具…...
为AI聊天工具添加一个知识系统 之113 详细设计之54 Chance:偶然和适配 之2
本文要点 要点 祖传代码中的”槽“ (占位符变量) 和 它在实操中的三种槽(占据槽,请求槽和填充槽, 实时数据库(source)中数据(流入 ETL的一个正序流程 行列并发 靶向整形 绑定变量 )…...
HTML5 面试题
1. HTML5 新增了哪些重要特性? 语义化标签:这些标签有助于提高页面的可读性和可维护性。多媒体支持:HTML5 引入了 和 标签,可以直接嵌入音频和视频文件,无需依赖插件。本地存储:引入了 localStorage 和 se…...
鸿蒙初学者学习手册(HarmonyOSNext_API14)_自定义动画API(@ohos.animator (动画) )
前言 在纯血鸿蒙中最具有用户特色的效果就是自定义的动画效果。在纯血鸿蒙中有多种定义方式,但是今天介绍的是ApI中的自定义动画。 注意: 动画本身具有生命周期,但是不支持在UIAbility的文件使用,简单而言就是不允许在UIAbility生命周期中…...
PINN求解一维burgers方程
PINN求解一维burgers方程 模型搭建网络与训练结果可视化对比实际结果 完整代码下载链接 PINN求解一维burgers方程 模型 搭建网络与训练 #########-------------- python求解一维burgers方程-------------------################## # -*- coding: utf-8 -*- import os os.envi…...
Linux系统配置阿里云yum源,安装docker
配置阿里云yum源 需要保证能够访问阿里云网站 可以先ping一下看看(阿里云可能禁ping,只要能够解析为正常的ip地址即可) ping mirrors.aliyun.com脚本 #!/bin/bash mkdir /etc/yum.repos.d/bak mv /etc/yum.repos.d/*.repo /etc/yum.repos…...
Android 动态加入Activity 时 manifest 注册报错解决。使用manifestPlaceholders 占位
需求如下: 项目 测试demo 有多个渠道,部分渠道包含支付功能,在主测试代码外,需要一个单独 Activity 调用测试代码。 MainActivityPayActivity渠道A包含不包含渠道B包含包含 因为支付功能需要引入对应的 moudule,因此…...
【找工作】C++和算法复习(自用)
文章目录 C头文件自定义排序函数stl 算法树状数组 自用随便记录 C 排序 stl 头文件 全能头文件: #include<bits/stdc.h>自定义排序函数 bool compare(const int &odd1,const int &odd2) {return odd1>odd2; }stl 枚举map map<int, strin…...
【相聚青岛】人工智能与材料国际学术会议即将召开
一、大会简介 人工智能与材料国际会议(ICAIM 2025) 官方网站:www.ic-aim.net 官方邮箱:icaim2025163.com 会议时间:2025年3.21-24 会议地点:中国青岛 会议检索:EI检索 截稿时间:2月…...
BFS 解决 FloodFill 算法(典型算法思想)—— OJ例题算法解析思路
目录 一、733. 图像渲染 - 力扣(LeetCode) 算法代码: 算法思路 基础参数 函数入口 检查条件 初始化 BFS BFS 填充过程 返回结果 复杂度分析 总结 二、200. 岛屿数量 - 力扣(LeetCode) 算法代码:…...
纷析云开源版- Vue2-增加字典存储到localStorage
main.js //保存字典数据到LocalStorage Vue.prototype.$api.setting.SystemDictType.all().then(({data}) > {loadDictsToLocalStorage(data) })新增 dictionary.js 放在 Utils文件夹里面 // 获取字典数据 export function getDictByType(dictType) {const dicts JSON.par…...
Python 爬虫selenium
1.selenium自动化 selenium可以操作浏览器,在浏览器页面上实现:点击、输入、滑动 等操作。 不同于selenium自动化,逆向本质是: 分析请求,例如:请求方法、请求参数、加密方式等。用代码模拟请求去实现同等…...
前端导出word文件,并包含导出Echarts图表等
基础导出模板 const html <html><head><style>body {font-family: Times New Roman;}h1 {text-align: center;}table {border-collapse: collapse;width: 100%;color: #1118FF;font-weight: 600;}th,td {border: 1px solid black;padding: 8px;text-align: …...
【复现DeepSeek-R1之Open R1实战】系列8:混合精度训练、DeepSpeed、vLLM和LightEval介绍
这里写目录标题 1 混合精度训练1.1 FP16和FP321.2 优点1.3 存在的问题1.4 解决办法 2 DeepSpeed3 vLLM3.1 存在的问题3.2 解决方法3.2.1 PagedAttention3.2.2 KV Cache Manager3.2.3 其他解码场景 3.3 结论 4 LightEval4.1 主要功能4.2 使用方法4.3 应用场景 本文继续深入了解O…...
从面试中的“漏掉步骤”谈自我表达与思维方式的转变
在今天的面试中,我遇到了一个让我深刻反思自己思维方式的问题。当面试官问到如何应对用户量和请求量逐渐增加时,我的回答遗漏了一些基础步骤,导致我给出了“我暂时想不出更好的反思”的回答。这一经历让我意识到,在面对问题时&…...
大模型面经:SFT和RL如何影响模型的泛化或记忆能力?
监督微调 (SFT) 和强化学习 (RL)都是目前大模型的基础模型后训练技术,像DeepSeek-R1、kimi等的训练方法都将两种技术应用到了极致。 如何去设计训练步骤(先SFT再RL,还是直接RL)都需要对SFT和RL的能力有较深刻的了解。 本篇就以面…...
力扣-回溯-17 电话号码的字母组合
思路 和之前的回溯不同的是,要遍历完所有的数字,并且在单层递归逻辑里需要遍历一整个字符串 代码 class Solution { public:vector<string> letters {"", "", "abc", "def", "ghi", "…...
大模型幻觉
1.什么是大模型幻觉? 在语言模型的背景下,幻觉指的是一本正经的胡说八道:看似流畅自然的表述,实则不符合事实或者是错误的。 幻觉现象的存在严重影响LLM应用的可靠性,本文将探讨大型语言模型(LLMs)的幻觉问题,以及解决幻觉现象的一些常见方法。 2.为什么需要解决LLM的…...
2025-02-20 学习记录--C/C++-PTA 7-27 冒泡法排序
一、题目描述 ⭐️ 二、代码(C语言)⭐️ /** * 冒泡法实现升序 */#include <stdio.h>int main() {int N, // 整数个数 6K, // 扫描遍数 2num, // 待排序的整数 2 3 5 1 6 4numArr[100], // 待排序的整数合集 2 3 5 1…...
RK3588配置成为路由器
文章目录 前言一、配置netplan二、安装hostapd1.创建hostapd.conf文件2.安装软件3.修改启动文件4.修改/etc/default/hostapd 文件 三、安装dnsmasq服务四、配置NET及重启验证五、常见问题总结 前言 RK3588开发板有两个网口,一个无线网卡。我需要配置为家用路由器模…...
【数据挖掘】--算法
【数据挖掘】--算法 目录:1. 缺失值和数值属性处理1缺失值处理: 2. 用于文档分类的朴素贝叶斯3. 分治法:建立决策树4. 覆盖算法建立规则5. 挖掘关联规则6. 线性模型有效寻找最近邻暴力搜索(Brute-Force Search)kd树&am…...
