深入探索c/c++函数(3)---虚成员函数调用的基本过程

学过c++一段时间都会知道,c++是依靠虚函数实现多态的,如下代码:

 呵呵,输出T-T~~~~~~

对于理解虚函数的实现原理,历来是一个c++新手到中手的必经之路之一,关于其实现原理,个人推荐《深入探索c++对象模型》这本书,

原理讲的很透彻的。现在分析一下一些主流的编译器的具体实现方式,并从汇编的角度来分析编译器的虚函数的实现原理(最近找c/c++工作,估计虚函数被问到可能性很高~~)。

首先c++标准仅仅规定了虚函数的行为,并没有规定这种行为的具体实现,但目前主流的编译器(vc,g++)在实现上达成了一定默契,都是通过在对象前4个字节安插一个虚表指针,

这个虚表指针指向对应类的虚表,在调用虚函数时,通过虚表指针查找虚表最终获得要调用的函数的,这也就是动态绑定的底层实现方式。

以下是vc10默认编译选项debug下上面程序的反汇编:

 

 

通过上述分析,类似*(int*)*(int*)p这样的表达式来获取虚表中函数方法大家应该明白了吧,这个地方确实是考察指针应用的基本功的。

void(*f)()=(void(*)())*(int*)*(int*)p;

f();

最终调用的是Derive::Print();很显然*(int*)(*(int*)p+4)是虚表中第二个函数地址地址值,如果有的话~~~~~

下面来看下,编译器生成的构造函数里到底做了些什么,

Derive::Derive:
01031127  jmp         Derive::Derive (10315B0h)

找到内存10315B0h处的汇编指令:

 

看一下Base::Base (1031131h);汇编代码

 

分析到这里,相信大家对虚函数调用有个基本的认识了,编译器在实现虚函数时,主要有以下步骤:

1 编译时,根据类的声明,生成一个虚函数表

2 创建对象时,编译器会在类的构造函数内安插一部分代码,用来初始化对象的虚表指针,一般(vc g++)在进入构造函数

  开始部分便安插代码。

3 当以指针或引用来调用虚函数时便激活动态绑定,实质是一个通过虚表指针查找函数的过程

 所以类似这样代码Derive(){memset(this,0,sizeof(Derive));}将是灾难性的~~~

由于虚函数的实现要借助构造函数,所以构造函数不能是虚拟函数~~~

 

最后介绍两个关于c++虚函数的hack的简单程序,以加深编对译器实现虚函数机制的了解~~~~

 很显然通过修改虚表指针来劫持程序,下面来通过修改虚表来劫持程序~~~~~~~~~~~~~~

 

 

 上面程序就是成功修改了编译器创建的虚表,可以真正算得上一个hack了~~~,上面程序vc9/10+win7 debug/release默认编译选项通过~~~~

如果您看懂上述两个程序,相信您对虚表的编译器实现的认识更加深刻了~~~~

原理就是这个样子了,在多继承情况下,可能麻烦一些,因为对象可能产生多个虚表指针,另外虚析构函数在虚表中布局,各个编译器差异也比较大,

也就是为什么com在实现时要有一个类似release的接口~~~~

 

 

 

好了,先写到这儿吧,有时间再补充~~~~~~~~~~~~~~~~

 

 

 

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值