C++虚函数调用简单分析
2022/2/23 9:21:41
本文主要是介绍C++虚函数调用简单分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
C++代码如下:
class parent_parent { public: virtual int print() const { return 1; } }; class sub : public parent_parent { public: int print() const override { return 0; } }; int main() { parent_parent* p = new sub; p->print(); delete p; return 0; }
通过使用反汇编可得到main
函数如下:
main: .LFB2: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 pushq %rbx subq $24, %rsp .cfi_offset 3, -24 movl $8, %edi call _Znwm@PLT # 此处调用operator new(unsigned long)@PLT分配内存 movq %rax, %rbx # 保存new的返回值到rbx寄存器 movq %rbx, %rdi # 复制返回值到rdi寄存器 call _ZN3subC1Ev # 此处调用sub::sub()构造函数 movq %rbx, -24(%rbp) movq -24(%rbp), %rax movq (%rax), %rax movq (%rax), %rdx # 将print函数指针移动至rdx movq -24(%rbp), %rax movq %rax, %rdi call *%rdx # 此处调用p->print()函数 movq -24(%rbp), %rax testq %rax, %rax je .L8 movl $8, %esi movq %rax, %rdi call _ZdlPvm@PLT .L8: movl $0, %eax addq $24, %rsp popq %rbx popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc
parent_parent构造函数如下:
_ZN13parent_parentC2Ev: .LFB5: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) leaq 16+_ZTV13parent_parent(%rip), %rdx # 加载虚表地址 + rip地址 + 16,即parent_parent::print()函数的地址,放入rdx寄存器 movq -8(%rbp), %rax movq %rdx, (%rax) # 放入rax内地址指向的内存,即new分配的内存 nop popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc
以及sub构造函数如下:
_ZN3subC2Ev: .LFB7: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi # 保存new地址 call _ZN13parent_parentC2Ev #调用parent_parent::parent_parent() leaq 16+_ZTV3sub(%rip), %rdx # 加载虚表地址 + rip地址 + 16,sub::print()函数的地址,放入rdx寄存器 movq -8(%rbp), %rax movq %rdx, (%rax) # 放入rax内地址指向的内存,即new分配的内存 nop leave .cfi_def_cfa 7, 8 ret .cfi_endproc
调用过程大致如下:
先调用new函数分配内存,然后调用父类构造器,获取父类虚函数地址。然后,查询子类虚表,覆盖父类函数。调用子类override的函数
虚表构成:
vtable of parent_parent:
vtable for parent_parent: .quad 0 .quad typeinfo for parent_parent # 类型信息地址 .quad parent_parent::print() const # print函数地址 .weak typeinfo for sub .section .data.rel.ro._ZTI3sub,"awG",@progbits,typeinfo for sub,comdat .align 8 .type typeinfo for sub, @object .size typeinfo for sub, 24
vtable for sub:
vtable for sub: .quad 0 .quad typeinfo for sub .quad sub::print() const .weak vtable for parent_parent .section .data.rel.ro.local._ZTV13parent_parent,"awG",@progbits,vtable for parent_parent,comdat .align 8 .type vtable for parent_parent, @object .size vtable for parent_parent, 24
所以根据以上原理,可以通过如下方式调用虚表中的函数:
#include <iostream> using namespace std; class subclass { public: virtual void print() { cout << "hello world !\n"; } }; int main() { subclass* sub = new subclass; // sub 指向的地址存放 vtable存放函数的地址 void* vtable16 = *(void**)sub; // 通过存放函数地址的地址取得函数地址 void* funcs = *(void**)vtable16; // 获取print函数的地址 auto print_ptr = (void(*)())(funcs); // 通过函数地址调用函数 print_ptr(); delete sub; return 0; }
这篇关于C++虚函数调用简单分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-24怎么切换 Git 项目的远程仓库地址?-icode9专业技术文章分享
- 2024-12-24怎么更改 Git 远程仓库的名称?-icode9专业技术文章分享
- 2024-12-24更改 Git 本地分支关联的远程分支是什么命令?-icode9专业技术文章分享
- 2024-12-24uniapp 连接之后会被立马断开是什么原因?-icode9专业技术文章分享
- 2024-12-24cdn 路径可以指定规则映射吗?-icode9专业技术文章分享
- 2024-12-24CAP:Serverless?+AI?让应用开发更简单
- 2024-12-23新能源车企如何通过CRM工具优化客户关系管理,增强客户忠诚度与品牌影响力
- 2024-12-23原创tauri2.1+vite6.0+rust+arco客户端os平台系统|tauri2+rust桌面os管理
- 2024-12-23DevExpress 怎么实现右键菜单(Context Menu)显示中文?-icode9专业技术文章分享
- 2024-12-22怎么通过控制台去看我的页面渲染的内容在哪个文件中呢-icode9专业技术文章分享