C++之虚函数、虚函数表

news2025/5/24 17:03:09

C++ 虚函数、虚函数表详解与实践

C++中虚函数是实现多态的重要技术,接下来将从汇编、以及gdb调试运行方面下手全面了解虚函数、虚函数表、以及虚函数调用。

原理初认识

  1. 一个由虚函数的类将会有一个虚函数表,且所有该类的实例化对象共享一个虚函数表
  2. 虚函数表将存在代码的.data.rel.ro段中(该段表示需要重定位的只读数据段),在代码(elf可执行文件)的.rela.dyn段中指出了需要重定位的条目。
  3. 每个有虚函数的类的实例化对象都有一个指向该类虚函数表的成员指针变量(编译器自动创建)。
  4. 虚函数的调用会根据对象中的虚函数表去找对应的虚函数地址,然后再去调用对应的虚函数。
  5. 虚函数表的初始化是在编译时期(编译器直接将对应的虚函数地址放入虚函数表中),实例化对象的指向虚函数表的指针初始化是在构造函数中,其中指向虚函数表的指针是实现动态多态的重要技术。
  6. 类的构造函数不能是虚函数(原因见下面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. 汇编代码分析

  1. 调用流程分析
    1. main函数中会先调用SON的构造函数。
    2. SON的构造函数中会调用父类Base的构造函数,然后返回SON的构造函数。
    3. SON构造函数中会进行虚函数表指针的初始化(将SON类的虚函数表首地址放入该对象的虚函数表指针中)。
    4. SON构造函数完成剩下其他初始化就返回到main函数中。
  • 由于是先调用父类的构造函数再初始化虚函数表指针,所以如果父类构造函数是虚函数的话,在子类对象虚函数表指针都还没初始化就去调用重写的虚构造函数显然是不行的(因为虚函数表指针都没初始化,怎么能调用到重写的虚构造函数)。

  • 对应的汇编代码截屏
    在这里插入图片描述

  • 虚函数表所对应的.data.rel.ro段,虚函数表地址为0x11d18,其中存放的重写的call函数地址为0xfd8.
    在这里插入图片描述

  • SON类重写的call函数汇编代码截屏(地址分配为0xfd8
    在这里插入图片描述

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

  • gdb调试时虚函数表中重写虚函数的实际地址截屏,可以看出确实被重定位了指向了重写的call函数的真实地址
    在这里插入图片描述

  1. 虚函数的调用
    在这里插入图片描述

    // 将实例化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, sp
     f1c:       f9000bf3        str     x19, [sp, #16]
     f20:       d2800100        mov     x0, #0x8                        // #8
     f24:       97ffff93        bl      d70 <_Znwm@plt>
     f28:       aa0003f3        mov     x19, x0
     f2c:       aa1303e0        mov     x0, x19
     f30:       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     x1
     f4c:       52800000        mov     w0, #0x0                        // #0
     f50:       f9400bf3        ldr     x19, [sp, #16]
     f54:       a8c37bfd        ldp     x29, x30, [sp], #48
     f58:       d65f03c0        ret
  • SONBase类构造函数、重写的call函数
0000000000000fd8 <_ZN3SON4callEv>:
     fd8:       a9be7bfd        stp     x29, x30, [sp, #-32]!
     fdc:       910003fd        mov     x29, sp
     fe0:       f9000fe0        str     x0, [sp, #24]
     fe4:       b0000000        adrp    x0, 1000 <_ZN3SON4callEv+0x28>
     fe8:       9101e001        add     x1, x0, #0x78
     fec:       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        nop
     ffc:       a8c27bfd        ldp     x29, x30, [sp], #32
    1000:       d65f03c0        ret

0000000000001004 <_ZN4BaseC1Ev>:
    1004:       d10043ff        sub     sp, sp, #0x10
    1008:       f90007e0        str     x0, [sp, #8]
    100c:       90000080        adrp    x0, 11000 <__FRAME_END__+0xfdbc>
    1010:       9134c001        add     x1, x0, #0xd30
    1014:       f94007e0        ldr     x0, [sp, #8]
    1018:       f9000001        str     x1, [x0]
    101c:       d503201f        nop
    1020:       910043ff        add     sp, sp, #0x10
    1024:       d65f03c0        ret

0000000000001028 <_ZN3SONC1Ev>:
    1028:       a9be7bfd        stp     x29, x30, [sp, #-32]!
    102c:       910003fd        mov     x29, sp
    1030:       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, #0xd18
    1044:       f9400fe0        ldr     x0, [sp, #24]
    1048:       f9000001        str     x1, [x0]
    104c:       d503201f        nop
    1050:       a8c27bfd        ldp     x29, x30, [sp], #32
    1054:       d65f03c0        ret
  • .data.rel.ro段,虚函数表
Disassembly of section .data.rel.ro:

0000000000011d08 <_ZTV3SON>:
        ...
   11d10:       00011d38        .inst   0x00011d38 ; undefined
   11d14:       00000000        udf     #0
   11d18:       00000fd8        udf     #4056
   11d1c:       00000000        udf     #0

0000000000011d20 <_ZTV4Base>:
        ...
   11d28:       00011d50        .inst   0x00011d50 ; undefined

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2301834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础学QT、C++(一)安装QT

目录 如何快速学习QT、C呢&#xff1f; 一、编译器、项目构建工具 1、编译器&#xff08;介绍2款&#xff09; 2、项目构建工具 二、安装QT 1、下载QT安装包 2、运行安装包 3、运行QT creator 4、导入开源项目 总结 闲谈 如何快速学习QT、C呢&#xff1f; 那就是项目驱动法&…

Python SMTP 实现邮件发送功能

发送邮件的流程 登录我们邮箱&#xff0c; 书写接收者的邮箱&#xff0c; 书写题目与内容&#xff0c;添加附件&#xff0c; 点击发送。 邮件协议 smtp 是邮件发送的协议。pop3 是邮件接收的协议。 smtplib模块用法 创建协议对象&#xff1a;smtpObj smtplib.SMTP() 创建…

低价窜货要如何管控

在竞争激烈的市场环境中&#xff0c;低价与窜货就像一对如影随形的“孪生兄弟”&#xff0c;给品牌的健康发展带来了极大的困扰。低价销售不仅压缩了合理的利润空间&#xff0c;破坏了市场的价格体系&#xff0c;还会引发恶性竞争&#xff0c;让整个市场陷入混乱无序的状态。而…

《动手学机器人学》笔记

目录 0.介绍1.概述&#xff5c;空间位置、姿态的描述&#xff08;33&#xff09;&#xff5c;《动手学机器人学》2.&#xff08;2&#xff09;-Robotics Toolbox①&#xff08;V10.4&#xff09;3.齐次坐标与变换矩阵4.一般形式的旋转变换矩阵5.&#xff08;轴角法&#xff09;…

国产编辑器EverEdit - 文本编辑器的关键特性:文件变更实时监视,多头编辑不掉坑

1 监视文件变更 1.1 应用场景 某些时候&#xff0c;用户会使用多个编辑器打开同一个文件&#xff0c;如果在A编辑器修改保存&#xff0c;但是B编辑器没有重新打开&#xff0c;直接在B编辑器修改再保存&#xff0c;则可能造成在A编辑器中修改的内容丢失&#xff0c;因此&#x…

化学蛋白质组学与药物靶点筛选:DARTS、LiP-MS、TPP、CETSA技术的深度解析

更多详情请看&#xff1a;LiP-MS药物靶点筛选技术 在药物研发的复杂过程中&#xff0c;药物靶点的筛选是关键环节之一。化学蛋白质组学技术的出现&#xff0c;为药物靶点筛选提供了强大的工具&#xff0c;化学蛋白质组学是一门研究细胞或组织中全部蛋白质的化学组成、结构、功…

为AI聊天工具添加一个知识系统 之113 详细设计之54 Chance:偶然和适配 之2

本文要点 要点 祖传代码中的”槽“ &#xff08;占位符变量&#xff09; 和 它在实操中的三种槽&#xff08;占据槽&#xff0c;请求槽和填充槽&#xff0c; 实时数据库&#xff08;source&#xff09;中数据(流入 ETL的一个正序流程 行列并发 靶向整形 绑定变量 &#xff09…

PINN求解一维burgers方程

PINN求解一维burgers方程 模型搭建网络与训练结果可视化对比实际结果 完整代码下载链接 PINN求解一维burgers方程 模型 搭建网络与训练 #########-------------- python求解一维burgers方程-------------------################## # -*- coding: utf-8 -*- import os os.envi…

Android 动态加入Activity 时 manifest 注册报错解决。使用manifestPlaceholders 占位

需求如下&#xff1a; 项目 测试demo 有多个渠道&#xff0c;部分渠道包含支付功能&#xff0c;在主测试代码外&#xff0c;需要一个单独 Activity 调用测试代码。 MainActivityPayActivity渠道A包含不包含渠道B包含包含 因为支付功能需要引入对应的 moudule&#xff0c;因此…

【相聚青岛】人工智能与材料国际学术会议即将召开

一、大会简介 人工智能与材料国际会议&#xff08;ICAIM 2025&#xff09; 官方网站&#xff1a;www.ic-aim.net 官方邮箱&#xff1a;icaim2025163.com 会议时间&#xff1a;2025年3.21-24 会议地点&#xff1a;中国青岛 会议检索&#xff1a;EI检索 截稿时间&#xff1a;2月…

BFS 解决 FloodFill 算法(典型算法思想)—— OJ例题算法解析思路

目录 一、733. 图像渲染 - 力扣&#xff08;LeetCode&#xff09; 算法代码&#xff1a; 算法思路 基础参数 函数入口 检查条件 初始化 BFS BFS 填充过程 返回结果 复杂度分析 总结 二、200. 岛屿数量 - 力扣&#xff08;LeetCode&#xff09; 算法代码&#xff1a;…

前端导出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)都是目前大模型的基础模型后训练技术&#xff0c;像DeepSeek-R1、kimi等的训练方法都将两种技术应用到了极致。 如何去设计训练步骤&#xff08;先SFT再RL&#xff0c;还是直接RL&#xff09;都需要对SFT和RL的能力有较深刻的了解。 本篇就以面…

2025-02-20 学习记录--C/C++-PTA 7-27 冒泡法排序

一、题目描述 ⭐️ 二、代码&#xff08;C语言&#xff09;⭐️ /** * 冒泡法实现升序 */#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开发板有两个网口&#xff0c;一个无线网卡。我需要配置为家用路由器模…

【数据挖掘】--算法

【数据挖掘】--算法 目录&#xff1a;1. 缺失值和数值属性处理1缺失值处理&#xff1a; 2. 用于文档分类的朴素贝叶斯3. 分治法&#xff1a;建立决策树4. 覆盖算法建立规则5. 挖掘关联规则6. 线性模型有效寻找最近邻暴力搜索&#xff08;Brute-Force Search&#xff09;kd树&am…

Huatuo热更新--如何使用

在安装完huatuo热更新插件后就要开始学习如何使用了。 1.创建主框渐Main 新建文件夹Main&#xff08;可自定义&#xff09;&#xff0c;然后按下图创建文件&#xff0c;注意名称与文件夹名称保持一致 然后新建场景&#xff08;Init场景&#xff09;&#xff0c;添加3个空物体…

基于Django快递物流管理可视化分析系统(完整系统源码+数据库+详细开发文档+万字详细论文+答辩PPT+详细部署教程等资料)

文章目录 基于Django快递物流管理可视化分析系统&#xff08;完整系统源码数据库详细开发文档万字详细论文答辩PPT详细部署教程等资料&#xff09;一、项目概述二、项目说明三、研究意义四、系统设计技术架构 五、功能实现六、完整系统源码数据库详细开发文档万字详细论文答辩P…

基于射频开关选择的VNA校准设计

活动发起人小虚竹 想对你说&#xff1a; 这是一个以写作博客为目的的创作活动&#xff0c;旨在鼓励大学生博主们挖掘自己的创作潜能&#xff0c;展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴&#xff0c;那么&#xff0c;快来参加吧&#xff01…