深入理解虚函数表

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

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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值