汇编视角分析C++虚函数实现原理

深入解析C++虚函数表:从内存布局到性能优化的多态实现机制 在面向对象编程中,多态是实现代码复用和扩展性的核心技术,它允许程序在运行时根据对象类型调用相应的方法。C++通过虚函数表(vtable)和虚表指针(vptr)实现运行时多态,编译器为每个包含虚函数的类生成一个函数指针数组,对象通过隐藏的vptr指向该表以实现动态绑定。这一机制虽然带来灵活性,但也引入了间接寻址开销,可能影响缓存局部性和内联优化。理解虚函数表的内存布局、构造函数与析构函数中的vptr初始化时机,以及多重继承下的复杂表结构,对于调试内存泄漏、解决菱形继承问题和优化性能至关重要。在实际工程中,合理 阅读详情

1.概述

        虚函数是c++语言非常重要的机制,日常的c++编程工作中经常使用虚函数,通过汇编视角来探究虚函数的实现原理,有助于深刻理解虚函数的内部机制。尤其要说明的是:c++语法规范并没有规定虚函数的具体实现方案,不同的编译器实现方式可以不同,本文基于arm32平台,g++编译器来分析虚函数表的实现机制,基本主流编译器的原理基本是相似的,所以理解g++实现方案再去分析其他编译器的实现也是类似的。

2.示例代码

class State {
public:
    State(int id) { 
        this->mId = id;
    }
    
    State(const State &state) {
        printf("State copy constructor\n");
    }
    
    virtual void dump() {
        printf("State mId:%d\n", mId);
    }
                                                                                                                                                           
public:
    int mId;
};

class AudioState : public State {
public:
    AudioState(int id) : State(id) {
    }

    void dump() {
        printf("AudioState mId:%d\n", mId); 
    }
};
int main(){
    printf("%d\n" ,sizeof(AudioState));
    State *ps = new AudioState(1);
    ps->dump();
    return 0;
}

代码编译和反汇编:

arm-linux-androideabi-g++ -pie -fPIE -o vtable vtable.cpp

arm-linux-androideabi-objdump vtable > vtable.txt

3. 虚函数调用实现基本原理

1. 如果是通过类指针调用相应的虚函数,通过vtable实现调用。

2. 如果子类重新实现了虚函数,vtable中对应项填写子类的函数(如上图),否则,填写父类德函数。

如下代码:

AudioState *ps = new AudioState(1);
ps->dump();

调用dump函数的时候,首先从AudioState对象的vptr指针拿到虚函数表(vtable),然后获取到虚函数表中dump函数对应的表项,所以最终调用到AudioState::dump函数。下面通过g++ arm汇编分析上述调用过程,帮助我们更深刻的理解虚函数实现原理。

4.虚函数调用实现

1. AudioState虚函数表在哪里构建?

答:AudioState构造函数里面

下面分析AudioState的汇编代码:

000014b0 <_ZN10AudioStateC1Ei>:
    14b0:   e92d4810    push    {r4, fp, lr}
    14b4:   e28db008    add fp, sp, #8
    14b8:   e24dd00c    sub sp, sp, #12

    //fp-16栈处存储对象指针,即对象首地址
    14bc:   e50b0010    str r0, [fp, #-16]

    //fp-20栈处存储构造函数的参数,即id参数
    14c0:   e50b1014    str r1, [fp, #-20]  ; 0xffffffec

    //以下两行将r4 = 10abc(1500地址处的值) + 14d0 = 11F8c
    14c4:   e59f4034    ldr r4, [pc, #52]   ; 1500 <_ZN10AudioStateC1Ei+0x50>
    14c8:   e08f4004    add r4, pc, r4

    14cc:   e51b3010    ldr r3, [fp, #-16]
    14d0:   e1a00003    mov r0, r3
    14d4:   e51b1014    ldr r1, [fp, #-20]  ; 0xffffffec
    14d8:   ebffffd0    bl  1420 <_ZN5StateC1Ei>

    //r3存储AudioState对象的首地址
    14dc:   e51b3010    ldr r3, [fp, #-16]

    //r2从如下地址取值:ffffffa4(1504处的值, -92的补码) + r4= 11F8c - (5c) = 11F30
    //r2 = [11F30] = 11c68, 11c68就是AudioState虚函数表
    14e0:   e59f201c    ldr r2, [pc, #28]   ; 1504 <_ZN10AudioStateC1Ei+0x54>
    14e4:   e7942002    ldr r2, [r4, r2]

    //r2 取AudioState虚函数表的第三项(前面存储了typeinfo相关项), r2 = 11c70
    14e8:   e2822008    add r2, r2, #8

    //将r2 11c70这个地址存储对象的首地址,即vptr = 11c70
    14ec:   e5832000    str r2, [r3]
    14f0:   e51b3010    ldr r3, [fp, #-16]
    14f4:   e1a00003    mov r0, r3
    14f8:   e24bd008    sub sp, fp, #8
    14fc:   e8bd8810    pop {r4, fp, pc}
    1500:   00010abc            ; <UNDEFINED> instruction: 0x00010abc
    1504:   ffffffa4            ; <UNDEFINED> instruction: 0xffffffa4

00011c68 <_ZTV10AudioState>:                                                                                                                               
   11c68:   00000000    andeq   r0, r0, r0
   11c6c:   00011c84    andeq   r1, r1, r4, lsl #25
   11c70:   00001508    andeq   r1, r0, r8, lsl #10    //1508即AudioState::dump
   11c74:   00000000    andeq   r0, r0, r0

00001508 <_ZN10AudioState4dumpEv>:
    1508:   e92d4800    push    {fp, lr}
    150c:   e28db004    add fp, sp, #4
    1510:   e24dd008    sub sp, sp, #8
    1514:   e50b0008    str r0, [fp, #-8]
    1518:   e51b3008    ldr r3, [fp, #-8]
    151c:   e5933004    ldr r3, [r3, #4]
    1520:   e59f2014    ldr r2, [pc, #20]   ; 153c <_ZN10AudioState4dumpEv+0x34>
    1524:   e08f2002    add r2, pc, r2
    1528:   e1a00002    mov r0, r2
    152c:   e1a01003    mov r1, r3
    1530:   ebffff14    bl  1188 <printf@plt>
    1534:   e24bd004    sub sp, fp, #4
    1538:   e8bd8800    pop {fp, pc}
    153c:   0000d434    andeq   sp, r0, r4, lsr r4

根据上面汇编代码14ec行,最终将r2(11c70)存入r3中指向地址,其中r2时虚函数表的地址,对应的表项内容:1508,对应的是AudioState::dump函数地址,说明g++编译器会把vptr放在对象的开端处。

2. 虚函数调用流程?

答:通过vptr找到对应的虚函数

main函数的汇编代码:

000013d4 <main>:
    13d4:   e92d4810    push    {r4, fp, lr}
    13d8:   e28db008    add fp, sp, #8
    13dc:   e24dd00c    sub sp, sp, #12
    13e0:   e3a00008    mov r0, #8
    13e4:   fa000094    blx 163c <_Znwj>
    13e8:   e1a04000    mov r4, r0
    13ec:   e1a00004    mov r0, r4
    13f0:   e3a01001    mov r1, #1
    13f4:   eb00002d    bl  14b0 <_ZN10AudioStateC1Ei>

    //将AudioState对象指针ps存储fp-16处
    13f8:   e50b4010    str r4, [fp, #-16]

    //ps指针读入r3
    13fc:   e51b3010    ldr r3, [fp, #-16]

    //vptr读入r3
    1400:   e5933000    ldr r3, [r3]

    //AudioState::dump虚函数地址读入r3,即1508
    1404:   e5933000    ldr r3, [r3]
    1408:   e51b0010    ldr r0, [fp, #-16]

    //跳转到dump函数执行
    140c:   e12fff33    blx r3
    1410:   e3a03000    mov r3, #0
    1414:   e1a00003    mov r0, r3
    1418:   e24bd008    sub sp, fp, #8
    141c:   e8bd8810    pop {r4, fp, pc}

注意:如果直接使用类对象调用虚函数,不会通过虚函数,汇编代码中可以看到回直接bl跳转相应对象的函数。

汇编视角下的C++编程实践:从函数调用到虚函数实现深度解析 汇编语言作为连接高级编程语言与计算机硬件的桥梁,是理解程序底层执行原理的关键技术。通过分析x86-64架构的指令集和内存模型,开发者可以深入理解编译器如何将C++代码转换为机器指令,掌握程序性能优化的底层逻辑。这种技术能力在性能调优、内存管理、并发编程等工程实践中具有重要价值,特别是在调试复杂性能瓶颈和理解语言特性实现机制时尤为关键。通过【函数调用栈帧】和【虚函数表】等核心机制的汇编级分析,开发者能够建立C++代码与机器指令之间的双向映射思维,从而在编译器优化、内存布局分析等场景中实现更精准的程序控制与问题 阅读详情

相关推荐

C++类逆向分析:从内存布局到虚函数机制的底层原理与实践

在C++编程中,理解对象的内存布局和虚函数机制是深入系统底层的核心技能。内存对齐原则决定了类实例在内存中的实际排布,直接影响程序性能和内存使用效率。虚函数表(vtable)作为实现多态的关键数据结构,通过虚函数表指针(vptr)将高级抽象映射到机器指令,这是C++对象模型的基石。掌握这些底层原理对于诊断内存泄漏、分析多继承对象布局错乱等复杂问题至关重要。通过GDB、objdump等工具进行逆向分析,开发者可以直观观察编译器如何安排内存、如何实现虚函数调用,从而在性能优化、安全研究和系统调试等应用场景中游刃有

weixin_31423955的博客 289

Linux c++反汇编源码细节解释说明

Linux c++反汇编源码细节解释说明

vily_lei的专栏 1280

深入剖析C++虚函数表内存布局与多态实现原理

虚函数表是C++实现运行时多态的核心机制,它本质上是一个按类生成的函数指针数组,每个含有虚函数的对象都通过虚函数表指针来访问。其工作原理是在编译时为每个类生成虚函数表,对象构造时初始化指向该表的指针,虚函数调用时通过此指针间接寻址。这种设计在提供灵活多态性的同时,也引入了额外的内存访问开销。理解虚函数表的内存布局对于性能优化至关重要,特别是在涉及缓存未命中的场景下。通过分析单继承、多重继承等典型情况下的内存布局,开发者可以更好地设计类层次结构,避免性能陷阱。本文将从内存视角深入解析虚函数表的构建逻辑与调用关

weixin_33695450的博客 1641

LINUX C/C++经典 (逆向,coredump异常调试等)

https://blog.youkuaiyun.com/xuzhina/article/details/8487094 https://www.jianshu.com/p/a5a11567158c https://blog.youkuaiyun.com/happylife1527/article/category/1197730 https://blog.youkuaiyun.com/txx_683/article/detai...

happylzs2008的专栏 828

深入C++虚函数表:从内存布局到多态实现原理与性能优化

多态是面向对象编程的核心概念,它允许通过基类接口调用派生类的具体实现,实现了代码的灵活性和可扩展性。其底层原理主要依赖于虚函数表(vtable)和虚函数指针(vptr)机制,编译器为每个包含虚函数的类生成一张函数地址表,对象则通过内部的vptr指向该表,在运行时动态解析函数调用。这项技术为大型软件系统的模块化设计提供了基础,广泛应用于框架设计、插件系统和游戏引擎等场景。在工程实践中,理解vtable的内存布局对于调试复杂继承体系、排查因虚函数调用引发的崩溃问题至关重要。本文结合GDB调试和反汇编,深入剖析了

weixin_33743248的博客 532

游戏逆向实战:C++内存管理与虚函数表解析

在软件逆向工程领域,理解程序在内存中的运行机制是核心基础。从计算机科学原理出发,程序编译后形成的二进制代码在内存中遵循特定的布局规则,这涉及到指针寻址、数据结构对齐和函数调用约定等底层概念。这些原理在游戏逆向中具有极高的技术价值,因为现代游戏引擎普遍采用C++编写,其面向对象特性如虚函数表(VTable)和多态机制,直接决定了游戏对象在内存中的组织方式。掌握内存布局分析和VTable解析,能够帮助开发者或安全研究人员定位关键游戏逻辑、数据结构和函数调用流,广泛应用于单机游戏分析、安全研究和软件调试等场景。本

weixin_30843121的博客 916

linux系统的gdb调试c++和反汇编

linux系统的gdb调试c++和反汇编

vily_lei的专栏 748

C++虚函数表(vtable)原理深度解析:从多态实现到内存布局与性能优化

在面向对象编程中,多态是实现代码复用和灵活性的核心技术,它允许通过基类接口调用派生类的具体实现。其底层原理依赖于动态绑定机制,即在运行时根据对象的实际类型确定调用的函数。C++通过虚函数表(vtable)这一高效的数据结构来实现运行时多态,每个包含虚函数的类都拥有一张虚函数表,其中按顺序存储了该类所有虚函数的地址。对象内部则隐含一个指向该表的指针(vptr)。当通过基类指针或引用调用虚函数时,编译器会生成通过vptr间接寻址vtable并获取正确函数地址的代码,从而实现动态绑定。这种机制在单继承、多重继承等

weixin_30846347的博客 314

从汇编的角度了解C++原理——虚函数

从汇编的角度了解C++原理——虚函数

weixin_45001971的博客 1206

从汇编角度看虚函数和普通成员函数的调用过程

本篇文章直入正题。 我们的类成员函数分为inline、virtual、static、normal。 **normal成员函数:**其地址和一般函数的地址没有区别,就是函数代码在内存中的真实地址,但其调用要绑定到一个实实在在的对象上。取其地址需要使用“&”运算符。 **virtual成员函数:**其地址指的是其在vtable中的位置;取其地址需要使用“&”运算符。 inline成员函数:在运行时会展开,虽然语言允许取其地址,但是没有太大意义。 static成员函数:其地址和普通全局函数的地址没有任何区别;其不依赖与类的对象而存在,也不依赖与类的对象而调用,只是作用域变成了类作用域,可以像取全局函

实战指南:在摩尔线程GPU上部署AI模型的完整流程

本文提供了一份详尽的实战指南,手把手教你如何在摩尔线程GPU上成功部署AI模型。内容涵盖从驱动安装、Docker环境配置,到核心的代码适配与模型运行全流程,并分享了性能调优技巧与常见避坑方法,旨在帮助开发者高效利用国产硬件进行AI应用开发。

weixin_29271263的博客 537

C++复习,继承与多态

测试文

feng_wu_ying_li的博客 378

【Rcode】生存分析: KM & COX回归& 随机森林& nomogram

本文使用了R——survival包下的lung数据进行方法测试: 1)KM 2) Cox回归:多因素 3)随机森林因子(根据cox回归结果) 4)nomogram ( 根据cox回归结果,建立了中位生存时间,1年5年生存率的概率计算) -------------------????????‍♀️本文只有干货,非常干!????----------------------------- 一、数据加载 #生存分析 library("survival") library("survminer") data("

weixin_43172152的博客 9273

【C++零开销抽象实现原理】:从汇编视角看性能优化的底层逻辑

深入解析C++内核性能优化的底层机制,揭示零开销抽象如何通过汇编级优化提升程序效率。涵盖模板、内联与RAII在系统级编程中的应用,展现编译器优化与硬件协同的关键优势,为高性能软件开发提供实用方法论,值得收藏。

StepLens的博客 882

全网最详细 解决戴维南定理的详细推导

替代定理,戴维南定理详细证明

m0_52555753的博客 3万+

MTK手机传感器驱动开发避坑指南:从SCP代码大小限制到Overlay加载流程详解

本文详细解析了MTK平台传感器驱动开发中的常见问题,包括SCP代码大小限制、Overlay加载流程及客制化传感器集成。通过实战案例和优化策略,帮助开发者高效解决MTK传感器驱动开发中的技术难题,提升开发效率。

weixin_42522374的博客 400

C++虚函数调用机制深度解析:从vtable到vcall thunk的实现原理

虚函数是C++实现运行时多态的核心机制,其底层通过虚函数表(vtable)和虚函数指针(vptr)实现动态绑定。编译器在编译时为每个包含虚函数的类生成vtable,其中存储了类型信息和虚函数地址,对象通过内部的vptr指向对应的vtable。当通过基类指针调用虚函数时,CPU会执行加载vptr、计算偏移和间接跳转的指令序列,实现多态调用。这一机制在多重继承和虚析构函数等复杂场景下,需要编译器生成vcall thunk进行this指针调整,确保函数调用的正确性。理解虚函数调用的底层原理,对于深入掌握C++对象

weixin_30907935的博客 378

(四) OpenClaw-Skill技能全面了解和自定义技能

OpenClaw Skills是提升AI代理功能性的关键模块。本文系统介绍了Skills的核心概念、获取渠道和创建方法。Skills作为标准化指令集合,能让OpenClaw从单纯理解升级为实际执行任务,相比传统System Prompt具有模块化、可复用等优势。用户可通过Clawhub官方商店、GitHub社区等渠道获取5700+现成技能,或按需开发自定义Skill。文章详细演示了创建"Hello World"示例技能的完整流程,包括编写Python脚本、配置SKILL.md文件、部署。

直到世界的尽头 8294

编译器优化视角下C++虚函数调用开销分析

虚函数是C++实现动态多态的核心机制,其通过虚函数表(vtable)和虚指针(vptr)在运行时动态确定调用的函数版本。然而,这种灵活性伴随着特定的运行时开销:每次虚函数调用需通过vptr定位虚表,再通过偏移量找到目标函数地址,形成多级间接寻址。此外,虚函数表的存在增加了对象内存占用(64位系统下每个对象多8字节存储vptr),且由于编译器无法在编译时确定具体调用的函数,虚函数通常无法内联优化。当频繁通过基类指针调用虚函数时,CPU需反复加载不同对象的虚表,破坏指令缓存和数据缓存的局部性。

2501_93395246的博客 572

【大数据技术干货】(还不懂数仓,数据中心?看这一篇就够了!)在实际的开发生产中,让你了解企业级数据中心整体架构(DB+ETL+ODS+DW+DM)的各个层次的过程详解,数据联系及功能区分

一 企业级数据中心架构 数据仓库的整体架构,各个系统的元数据通过ETL同步到操作性数据仓库ODS中,对ODS数据进行面向主题域建模形成DW(数据仓库),DM是针对某一个业务领域建立模型,具体用户(决策层)查看DM生成的报表。 二 DB 三 ETL(Extract-Transform-Load) ETL是将业务系统的数据经过抽取、清洗转换之后加载到数据仓库的过程,目的是将企业中的分散、零乱、标准不统一的数据整合到一起,为企业的决策提供分析依据。 ETL是BI项目重要的一个环节。 通常情况下,在BI项目中ET

liuchunhang的博客 1288

【XSRP软件无线电】基于软件无线电平台的QPSK频带通信系统设计(含有完整工程资料获取链接)

软件无线电(Software Defined Radio, SDR)是一种无线通信系统,它通过软件来定义和实现无线电的功能,而不是依赖于传统的硬件电路。SDR平台的核心思想是将传统无线电系统中的大部分硬件功能,如调制解调、信号处理、频率选择等,通过软件来实现。这种设计理念使得SDR具有高度的灵活性和可重配置性,能够适应不同的通信标准和频段,快速适应新的通信技术。

嵌入式技术开发 3280

C++虚函数与多态底层机制:从内存布局到汇编指令的深度解析

在面向对象编程中,多态是实现代码复用和灵活性的核心技术,它允许通过基类接口调用派生类的具体实现。其底层原理依赖于虚函数表(vtable)和虚函数指针(vptr)机制,编译器在编译时为每个包含虚函数的类生成一张虚函数表,存储函数地址,而每个对象则隐式包含一个指向该表的vptr。这种设计实现了动态绑定,即在运行时根据对象的实际类型确定调用的函数,从而支持接口与实现的分离。从技术价值看,多态提升了代码的可扩展性和可维护性,是设计模式(如工厂、策略模式)的基石。在应用场景上,它广泛用于框架设计、插件系统、回调机制等

weixin_32075843的博客 234

华为OD机试 双机位A卷 - 流量波峰 (C++ & Python & JAVA & JS & GO)

华为OD机试双机位A卷 流量波峰,提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解

qq_45776114的博客 1377

【C++底层原理系列】:从汇编角度看this绑定实现机制

深入解析成员函数指针的 this 绑定机制,从汇编层面揭示C++对象调用的底层实现。涵盖this指针隐式传递、成员函数调用约定及多态场景下的寄存器操作,帮助开发者理解性能开销与对象模型关系,值得收藏。

LogicGlow的博客 413

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

C++ 对象模型:虚函数表的底层结构与多态实现

多态作为面向对象三大核心特性(封装、继承、多态)之一,其可以分为静态多态(模板,重载,重定义)和动态多态。理解动态多态的底层实现原理对于掌握C++对象模型有重要意义。本文将从虚函数表、内存布局、调用的汇编代码等底层结构出发,深入解析动态多态的实现机制。

qq_40529346的博客 804

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++多态背后的秘密(虚函数表结构与调用机制详解)

深入解析c++多态的实现原理虚函数表,揭示对象动态调用背后的运行机制。涵盖虚函数表结构、内存布局与调用流程,适用于继承与重写场景,提升程序扩展性与灵活性。理解多态底层逻辑的关键指南,值得收藏。

CompiWander的博客 1063

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

上一篇: arm push/pop/b/bl汇编指令
下一篇: ELF格式分析动态链接原理
OS技术解密
博客等级 码龄16年 3760粉丝 189原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值