跟我一起学习C++虚函数--第一篇
来源:互联网 发布:淘宝尾款怎么付 编辑:程序博客网 时间:2024/05/20 05:28
我们知道,虚函数作为C++实现多态的方式,具有强大的RTTI(RunTime Type Identification)功能。虚函数使用起来比较简单,但是也很容易出错。本系列将带着你一步一步了解虚函数的内部实现机制,在掌握原理后,我相信你会对虚函数以及C++本身会有进一步的认识和理解。
注:本系列所有的关于C++虚函数的探索都是在GCC平台上进行的。
一、带有虚函数的对象内存布局
让我们先看一段代码:
01
#include <iostream>
02
using
namespace
std;
03
04
class
Test
05
{
06
public
:
07
virtual
void
f(){cout <<
"Func:f()"
<< endl;};
08
09
public
:
10
int
a;
11
};
12
13
int
main()
14
{
15
cout <<
sizeof
(Test) << endl;
//输出:8
16
Test test;
17
cout << &test <<
"\t"
<< &test.a << endl;
//输出:0xbf91e508 0xbf91e50c
18
return
0;
19
}
1)编译器在包含虚函数的类中插入了虚指针vptr,大小为4。
2)对包含虚函数的对象,虚指针被安放在对象的起始位置。如下图所示:
这个例子我们只是简单地介绍了包含虚函数对象的成员内存布局,下面我们慢慢深入看看虚表的结构。还是从例子入手:
01
#include <iostream>
02
using
namespace
std;
03
04
class
Test
05
{
06
public
:
07
virtual
void
f(){cout <<
"Func:f()"
<< endl;};
08
virtual
void
g(){cout <<
"Func:g()"
<< endl;};
09
virtual
void
h(){cout <<
"Func:h()"
<< endl;};
10
private
:
11
int
a;
12
};
13
14
int
main()
15
{
16
Test test;
17
typedef
void
(*Func)(
void
);
//定义个函数指针宏
18
cout <<
"vptbl address:"
<< (
int
*)(&test) << endl;
//输出:vptbl address:0xbfc85494
19
cout <<
"func1 address:"
<< (
int
*)*(
int
*)(&test) << endl;
//输出:func1 address:0x8048988
20
21
Func pFunc = (Func)*((
int
*)*(
int
*)(&test));
22
pFunc();
//输出:Func:f()
23
24
pFunc = (Func)*((
int
*)*(
int
*)(&test)+1);
25
pFunc();
//输出:Func:g()
26
27
pFunc = (Func)*((
int
*)*(
int
*)(&test)+2);
28
pFunc();
//输出:Func:h()
29
30
return
0;
31
}
这里对上面的操作进行简单解释,主要是指针方面的:
1)取得虚函数表地址:(int *)(&test)。
在前面我们已经讨论过,对象的起始地址为虚指针地址。对对象地址进行int *强制转换实际上就是得到虚指针,而虚指针的内容就是虚函数表地址。
2)取得虚函数表中第一个虚函数地址:(int *)*(int *)(&test)。
取得虚表地址后,再次进行取址便得到了第一个虚函数地址。
3)调用虚函数表中的函数(Func)*((int *)*(int *)(&test))。
取得第一个虚函数地址后,进行强制类型转换,便得到了函数地址。
好,到这里相信大家应该对带有虚函数的对象的内存布局有一个较为清楚的了解了吧。下一章我们讨论在继承情况下的内存布局情况。
参考文献:
1.《深度探索C++对象模型》
- 跟我一起学习C++虚函数--第一篇
- 跟我一起学习C++虚函数--第二篇
- 跟我一起学习C++虚函数--第三篇
- 跟我一起学习C++虚函数--第四篇
- 跟我一起学习C++虚函数--第五篇
- 跟我一起学习C 第一节
- 跟我一起学C语言(第五天)
- 跟我一起学C语言(第七天)
- 跟我一起学C语言(第九天)
- 《跟我一起学C++》
- 跟我一起学习Makefile
- 跟我一起学习VIM
- 跟我一起学习VIM
- 跟我一起学习VIM
- 跟我一起学习VIM
- 跟我一起学JS第一天
- C++跟我一起透彻理解虚函数表
- 跟我一起写Makefile:使用函数
- nagios安装遇到的问题及解决
- 浅析GCC下C++多重继承 & 虚拟继承的对象内存布局
- Recordset.Open参数说明
- R-2.15.1 在 fedora16 上的安装(Can't find X11 headers and libs)
- 教你用Windows API 写一个Thread类(不使用static哦)------(1)
- 跟我一起学习C++虚函数--第一篇
- Nagios监控项目配置
- 非缓存 重定向到文件(未验证,仅作标记)
- 跟我一起学习C++虚函数--第二篇
- 跟我一起学习C++虚函数--第三篇
- 两位企业经营实践大师干了什么
- oracle 函数大全 之 substr
- 双线DNS解析
- 关于vmlinux,vmlinuz,bzImage,zImage的区别和联系