从汇编层面深度剖析C++基本对象布局
作者:网络转载 发布时间:[ 2014/7/2 11:24:38 ] 推荐标签:Net 软件开发
6. 静态成员函数
是否还记得静态函数成员不能使用非静态变量成员?为什么不能使用非静态变量成员呢?原因很简单,是因为静态函数成员没有this参数。C++的静态函数成员,和静态数据成员一样,是属于类的,而不是属于对象的,访问它们时,不需要使用任何现成的对象,直接使用<class-name>::<member>形式即可,所以它的函数不需要this指针。
下面point::get_instances()函数反汇编的结果:
(gdb)disassemble/m_ZN5point13get_instancesEv
Dumpofassemblercodeforfunction_ZN5point13get_instancesEv:
17staticintget_instances()
0x08048530<_ZN5point13get_instancesEv+0>:pushebp
0x08048531<_ZN5point13get_instancesEv+1>:movebp,esp
18{
19returnins_cnt;
0x08048533<_ZN5point13get_instancesEv+3>:moveax,ds:0x804a01c
20}
0x08048538<_ZN5point13get_instancesEv+8>:popebp
0x08048539<_ZN5point13get_instancesEv+9>:ret
Endofassemblerdump.
|
在函数体内,没有从堆栈里面读取任何参数信息,我们可以认为该函数是没有带参数,即它的参数型类为void。其实我们可以从调用该函数的地方去验证。下面是main函数反汇编的部分结果:
39 x.move(10, 10);
0x080484ba <main+38>: mov DWORD PTR [esp+0x8],0xa
0x080484c2 <main+46>: mov DWORD PTR [esp+0x4],0xa
0x080484ca <main+54>: lea eax,[esp+0x14]
0x080484ce <main+58>: mov DWORD PTR [esp],eax
0x080484d1 <main+61>: call 0x804853a <_ZN5point4moveEii>
40
41 int p_ins_cnt = point::get_instances();
0x080484d6 <main+66>: call 0x8048530 <_ZN5point13get_instancesEv>
0x080484db <main+71>: mov DWORD PTR [esp+0x1c],eax
在x.move(10, 10);调用时,它使用了两个mov …, 0xa将常量10压入堆栈中,作为_ZN5point4moveEii函数的第二和第三个参数,第一个当然是this拉。
而x.move(10, 10) 调用完后,它接着call _ZN5point13get_instancesEv,说明_ZN5point13get_instancesEv函数不带任何参数。
因此point::get_instances()函数翻译成C语言代码相应如下:
[cpp] view plaincopyprint?
int point::get_instances(void)
{
return point_ins_cnt;
}
7. 总结
不考虑C++虚函数,继承等语法功能后的C++基本对象内存布局模式格外简单。具有以下特点:
1)class内定义的非静态数据成员,它将占用对象的内存,它的布局类似于一种相应的结构体定义相应的字符。
2) class内定义的静态数据成员,它是类变量,每种类只有的一份,它以全局变量的身份挤身于全局变量列表。当然g++可根据它的初始化值是否为0来安排它放在.bss节区还是.data节区。
3)非静态函数成员,不占用对象的内存,它经C++编译器处理后,它是一个全局函数,它的第一个参数为this指针,其余参数类型和名字,与用户定义的一致。
4) 静态函数成员,同样不占用对象的内存,它经C++编译器处理后,它是一个全局函数,它没有this指针,它的参数类型和名字与用户定义的一致。
8. 给读者的问题
看到这样,你是不是明白了C++基本对象内存布局的里里外外?其实,上面的分析中,还少考虑了一种函数,那是const成员函数。你能说出成员函数和const成员函数在反汇编后的区别吗?成员函数和const成员函数的重载关系能否转换成函数参类间类型不同的重载关系。
39 x.move(10, 10);
0x080484ba <main+38>: mov DWORD PTR [esp+0x8],0xa
0x080484c2 <main+46>: mov DWORD PTR [esp+0x4],0xa
0x080484ca <main+54>: lea eax,[esp+0x14]
0x080484ce <main+58>: mov DWORD PTR [esp],eax
0x080484d1 <main+61>: call 0x804853a <_ZN5point4moveEii>
40
41 int p_ins_cnt = point::get_instances();
0x080484d6 <main+66>: call 0x8048530 <_ZN5point13get_instancesEv>
0x080484db <main+71>: mov DWORD PTR [esp+0x1c],eax
在x.move(10, 10);调用时,它使用了两个mov …, 0xa将常量10压入堆栈中,作为_ZN5point4moveEii函数的第二和第三个参数,第一个当然是this拉。
而x.move(10, 10) 调用完后,它接着call _ZN5point13get_instancesEv,说明_ZN5point13get_instancesEv函数不带任何参数。
因此point::get_instances()函数翻译成C语言代码相应如下:
[cpp] view plaincopyprint?
int point::get_instances(void)
{
return point_ins_cnt;
}
7. 总结
不考虑C++虚函数,继承等语法功能后的C++基本对象内存布局模式格外简单。具有以下特点:
1)class内定义的非静态数据成员,它将占用对象的内存,它的布局类似于一种相应的结构体定义相应的字符。
2) class内定义的静态数据成员,它是类变量,每种类只有的一份,它以全局变量的身份挤身于全局变量列表。当然g++可根据它的初始化值是否为0来安排它放在.bss节区还是.data节区。
3)非静态函数成员,不占用对象的内存,它经C++编译器处理后,它是一个全局函数,它的第一个参数为this指针,其余参数类型和名字,与用户定义的一致。
4) 静态函数成员,同样不占用对象的内存,它经C++编译器处理后,它是一个全局函数,它没有this指针,它的参数类型和名字与用户定义的一致。
8. 给读者的问题
看到这样,你是不是明白了C++基本对象内存布局的里里外外?其实,上面的分析中,还少考虑了一种函数,那是const成员函数。你能说出成员函数和const成员函数在反汇编后的区别吗?成员函数和const成员函数的重载关系能否转换成函数参类间类型不同的重载关系。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南