您的位置:首页 > 编程语言 > C语言/C++

跟我一起学习C++虚函数--第一篇

2012-07-17 20:44 176 查看
我们知道,虚函数作为C++实现多态的方式,具有强大的RTTI(RunTime Type Identification)功能。虚函数使用起来比较简单,但是也很容易出错。本系列将带着你一步一步了解虚函数的内部实现机制,在掌握原理后,我相信你会对虚函数以及C++本身会有进一步的认识和理解。

注:本系列所有的关于C++虚函数的探索都是在GCC平台上进行的。

一、带有虚函数的对象内存布局

让我们先看一段代码:

View Code

#include <iostream>
using namespace std;

class Test
{
public:
virtual void f(){cout << "Func:f()" << endl;};
virtual void g(){cout << "Func:g()" << endl;};
virtual void h(){cout << "Func:h()" << endl;};
private:
int a;
};

int main()
{
Test test;
typedef void (*Func)(void);//定义个函数指针宏
cout << "vptbl address:" << (int *)(&test) << endl;//输出:vptbl address:0xbfc85494
cout << "func1 address:" << (int *)*(int *)(&test) << endl;//输出:func1 address:0x8048988

Func pFunc = (Func)*((int *)*(int *)(&test));
pFunc();//输出:Func:f()

pFunc = (Func)*((int *)*(int *)(&test)+1);
pFunc();//输出:Func:g()

pFunc = (Func)*((int *)*(int *)(&test)+2);
pFunc();//输出:Func:h()

return 0;
}


从输出结果我们可以看出,我们可以直接获取到虚函数表以及表中每一个函数的地址。如下图所示:

  


这里对上面的操作进行简单解释,主要是指针方面的:
1)取得虚函数表地址:(int *)(&test)。
在前面我们已经讨论过,对象的起始地址为虚指针地址。对对象地址进行int *强制转换实际上就是得到虚指针,而虚指针的内容就是虚函数表地址。

2)取得虚函数表中第一个虚函数地址:(int *)*(int *)(&test)。
取得虚表地址后,再次进行取址便得到了第一个虚函数地址。

3)调用虚函数表中的函数(Func)*((int *)*(int *)(&test))。
取得第一个虚函数地址后,进行强制类型转换,便得到了函数地址。

好,到这里相信大家应该对带有虚函数的对象的内存布局有一个较为清楚的了解了吧。下一章我们讨论在继承情况下的内存布局情况。

参考文献:
1.《深度探索C++对象模型》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: