博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用内联汇编调虚函数 理解VTABLE原理
阅读量:4123 次
发布时间:2019-05-25

本文共 1299 字,大约阅读时间需要 4 分钟。

虚函数和动态绑定是C++面向对象编程的核心内容之一。要理解C++虚函数的调用本质,就不得不说VPTR和VTABLE。所有拥有虚函数的C++类的大小都比可看到的内容多至少4个字节(如果派生树中存在多继承,就可能多于4个字节),这多出来的4个字节就是VPTR,它位于每个实例的最前方。VPTR的内容就是一个unsigned int的地址,指向一个内存区域,而这个被指向的内存区域就是VTABLE,所谓虚函数地址表。每个拥有虚函数的类都拥有一张VTABLE,里面是一个函数指针数组,每4个字节为一个单位,指向虚函数的入口地址。C++在调用虚函数时,首先要通过这个类的实例内容看到VPTR,从而找到VTABLE,然后根据要调用的虚函数,取相应偏移地址的内容,从而把调用转到这个位置。

以下是我用VC内联汇编编写的一个手工模拟此过程的示例,通过例子中的代码可以清楚的看出虚函数的调用过程。
首先定义一个类,它有一个虚函数:

class
 Class1
{
 
int i;
public:
 Class1(
int i) this->= i; }
 
virtual void Print(int a, int b) { printf("i=%d a=%d b=%d ", i, a, b); }
}
;

如果取这个类的大小,可以看到结果是8而不是4。

下面声明这个类的一个实例,并取得其VTABLE中第一个元素的值:

 Class1
*
 pC 
=
 
new
 Class1(
1
);
 
int
 addr 
=
 
*
(
int
*
)(
*
(
int
*
)pC);

注意取值的这一行运用了复杂的强制类型转换。我把它拆开解释一下。首先是取得对象pC的前四个字节的内容,只要把pC转换成int*然后直接取值就行了:

 *(int*)pC
下一步是把取得的这个值当作是一个指针,也就是再进行一次强制类型转换:
 (int*)(*(int*)pC)
最后取这个指针所指内存的内容,也就是VTABLE中第一个函数的地址了:
 *(int*)(*(int*)pC)
取得这个地址以后,下面就用汇编代码来调用这个地址所指的函数:

 __asm
 
{
  mov ecx, pC;
  push 
3;
  push 
2;
  call addr;
 }

注意调用类的非静态成员函数时需要先把对应实例的地址放到ECX寄存器中,也就是平常所说的“隐藏参数”了,然后为函数Print压两个参数进栈,根据运行的结果可以明显看出来调用类成员函数时也是从右向左压栈的,最后用call语句调用函数。不难发现调用类成员函数时也是由被调用者负责弹栈,/*看来类成员函数是不能声明为参数个数可变的函数了*/。
最后,执行程序,得到结果:
i=1 a=2 b=3 

 

//===========================================

上面写的确实有点问题,类成员函数是可以声明为参数个数可变的函数的,并且当参数个数可变时,编译得到的代码自动转换为类似于__cdecl的调用方式:从右向左压栈,this指针也入栈,并且由调用者负责弹栈。

转载地址:http://bcopi.baihongyu.com/

你可能感兴趣的文章
C++ Primer Plus 学习笔记 第十一章 使用类 运算符重载 友元函数
查看>>
C++ Primer Plus 学习笔记 第十一章 类的自动转换和强制类型转换 转换函数
查看>>
C++ Primer Plus 学习笔记 第十二章 动态内存和类
查看>>
Vue + Django2.0.6 学习笔记 6-3,在Vue中展示商品分类数据
查看>>
Vue+Django 2.0.6 学习笔记 5.10 drf过滤
查看>>
Django-filter报错:__init__() got an unexpected keyword argument 'name'
查看>>
Django 2.0.6 学习笔记 13.8 xamin excel导入插件
查看>>
C++ Primer Plus 学习笔记 第十二章 对象指针小总结下
查看>>
C++ Primer Plus 学习笔记 第十三章 类继承
查看>>
码云 初始本地仓库, original line endings in your working directory,failed to push some refs to git
查看>>
C++ Primer Plus 学习笔记 十三章 多态公有继承 虚函数,静态联编与动态联编 协变
查看>>
Vue + Django 2.0.6 学习笔记 5.11 过滤2 搜索 排序
查看>>
码云 push时提示 DeployKey does not support push code fatal: Could not read from remote repository.
查看>>
C++ Primer Plus 学习笔记 第十三章 protected 纯虚函数 继承和动态内存分配 类设计回顾
查看>>
Vue + Django 2.0.6 学习笔记 6.5
查看>>
C++ Primer Plus 学习笔记 第十四章 valarray类 私有继承
查看>>
关于无法使用HttpRequester使token自动生成key和postman的使用
查看>>
C++ Primer Plus 学习笔记 第十四章 保护继承 多重继承
查看>>
C++ Primer Plus学习笔记 第十四章 类模板
查看>>
C++ Primer Plus 学习笔记 第十四章 指针类型模板 模板多功能型 模板的具体化
查看>>