深入理解虚函数表

来源:互联网 发布:vm虚拟机破解版 mac 编辑:程序博客网 时间:2024/06/06 18:53

在深入研究虚函数表之前,我们先思考几个问题:

1、虚函数表存储在什么地方

2、虚函数表中的内容是什么时候确定的

3、虚函数表的指针什么时候赋值

在windows系统下,虚函数表存储在只读数据段(.rdata),也就是说虚函数表在编译阶段就已经形成了,虚函数表指针是在构造函数中赋值的。

相关解释参考 C++虚函数表详细解释及实例分析

但是我还是想更加深入的探索一下虚函数表的实现机制:

测试代码如下:

#include <iostream>using namespace std;class Base1{public:Base1(){}Base1(int i){ _i = i; }~Base1(){}virtual int get(){ return _i; }virtual void set(){}private:int _i;};class Base2{public:Base2(){}Base2(int i){ _i = i; }~Base2(){}virtual int get(){ return _i; }virtual int get2(){ return _i; }virtual void set2(){}private:int _i;};class Sec:public Base1,public Base2{public:Sec(){}Sec(int i, int j) :Base1(i),Base2(j), _j(j){}~Sec(){}int get(){ return _j; }int get2(){ return _j; }//void set(){}private:int _j;};int main(){typedef int(*Fun)();Base1 b(1);Base2 b2(2);Sec  s(3, 4);Base1 *p = &s;int k = p->get();Base2 *q = &s;int m = q->get();int n = (unsigned)&s;return 0;}

在Debug下发现 n 与p的值是相等的,但是p与q的地址是不同的。而且有意思的是在Sec的虚函数表中,Base1的get函数地址与Base2的get函数地址其实是不一样的,但是在运行过程中,都调用了Sec的get函数。图示如下:

 到底是为什么呢?

于是便想通过ida的强大的反汇编功能就可以了解一二。


以上代码是main函数下的一部分片段,红线部分是调用Sec类的构造函数,跟进去看一下




可以看到在Sec类的构造函数中,分别执行Base1和Base2的构造函数,然后再将Base1和Base2的虚函数表的地址赋值给变量s。其实,在Base1和Base2构造函数中也是先执行构造函数然后再将虚函数表的指针赋值,只不过被后来的虚函数表指针给覆盖掉了。然后再看看虚函数表的情况:


带着蓝字中的问题,再看一下虚函数表的具体情况:

以下是Base2的情况:



可以看到j_?get@Sec@@UAEHXZ就是Base1虚函数表中get函数的地址。说明,虽然在Sec的虚函数表中,Base1的get函数地址与Base2的get函数地址不一样,但是最终指向的函数地址是一致的。

0 0
原创粉丝点击