




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 /target:从内存角度熟悉VC+面向对象机制作为MFC编程的基础/author:by Reduta/descritption:IA32 + win sp3 + vc6.0 /OD 1.10一:构造函数的之争1.1:构造函数是有返回值的返回当前对象的this指针 基本打开每一本C+的教程,都会对构造函数有如此类似的描述“构造函数无返回值,可进行函数重载”,但是事实上又如何呢?答案是构造函数具备返回值,返回值为当前对象的this指针。编写如下代码:/示例#include <iostream>using namespace std;class testint a;public:te
2、st();void show();test:test()a=1;void test:show()cout<<a<<endl;int main()test hacker;/调用构造函数 eax为构造函数返回值_asm/借助返回值修改对象数据成员mov dword ptr ss:eax,2/查看修改是否成功hacker.show();return 0; 执行上面的程序,对象hacker的数据成员a的值将变为2,将程序载后OD,看关键代码:00401588 8D4D FC lea ecx,dword ptr ss:ebp-4;hacker的this指针0040158B E8
3、AFFCFFFF call api.0040123F;构造函数00401590 36:C700 02000000 mov dword ptr ss:eax,2;修改对象的数据成员00401597 8D4D FC lea ecx,dword ptr ss:ebp-4;hacker的this指针0040159A E8 0FFCFFFF call api.004011AE;hacker的show函数 执行到401590时,注意观察eax与ecx的值,如图1所示,eax和ecx的值是一样的,说明构造函数的确是有返回值,且返回值为当前对象的this指针,重新载入OD,在构造函数处,F7跟进,如图2所示,
4、用红框标识的为关键代码,即构造函数,最后都会执行一句指令,将当前对象的this指针保存到eax中,而eax是函数的返回值。不管是debug版的程序还是release版程序都会执行类似的指令,即无论何时,构造函数将返回当前对象的this指针。 1.2:构造函数并不一定是函数inline内联的影响 看到构造函数并不一定是函数,你可能觉得可笑,这主要受inline内联的影响。 如果将上面的构造函数定义在类作用域内即如下代码,此时测试release版本程序会发现,执行结果依然为1,如图3所示,即修改未遂,release版与debug版程序的最大区别在于代码优化,使用release版,基本所有的inli
5、ne函数都能得到扩展,即此时构造函数并不是一个函数,而只是指令扩展。将release版程序载入OD,跟进主函数,发现如下代码:00401020 55 push ebp00401021 8BEC mov ebp,esp00401023 51 push ecx00401024 C745 FC 01000000 mov dword ptr ss:ebp-4,1;构造函数代码处0040102B 36:C700 02000000 mov dword ptr ss:eax,200401032 8D4D FC lea ecx,dword ptr ss:ebp-400401035 E8 C6FFFFFF ca
6、ll api.004010000040103A 33C0 xor eax,eax0040103C 8BE5 mov esp,ebp0040103E 5D pop ebp0040103F C3 retn 并没有将test()构造函数进行显示调用,此时的test()构造函数只有一句代码即mov dword ptr ss:ebp-4,1。 1.3:C+初始化的过程 几乎每本C+的教程都会告诉我们对象是有构造函数初始化数据成员,但是并不能系统的告诉我们初始化的具体过程,本部分着重分析C+面向对象数据成员初始化的一般过程及各种初始化方式的特点。 首先来讨论一下C+初始化的一般方法全局对象初始化、初始化列
7、表、构造函数、static成员的初始化。static数据成员是在类内声明,类外定义的,在定义的时候并不遵守访问限定符的限定,一旦定义,static数据成员即遵守访问限定的限制,这里不过分讨论static成员,我们来看一下剩余的几种初始化方式,它们实际上组成了如图4的初始化层次结构。/全局初始化过程.class testint a;public:test()a=1;cout<<"constructor!"<<endl;test cao;int main()cout<<"in main!"<<endl;test
8、 dan;return 0; 执行程序如图5所示,即在执行main函数之前,需要先调用构造函数初始化全局对象cao,这个过程是有系统自动完成的,调用的函数为_cinit(),这是在CRT中的一个函数,有mscvrt.dll提供。/初始化列表无序进行.class testint a;const int b;int c;char d;public:test():a(1),c(2),d(0x61),b(10);int main()test hacker;return 0; 初始化列表是C+提供的一种用于初始化const数据常量的,同时他也能初始化非const数据常量,上面的代码反汇编后,主要的代码如
9、下:0040108A 894D FC mov dword ptr ss:ebp-4,ecx0040108D 8B45 FC mov eax,dword ptr ss:ebp-400401090 C700 01000000 mov dword ptr ds:eax,1;a=100401096 8B4D FC mov ecx,dword ptr ss:ebp-400401099 C741 04 0A000000 mov dword ptr ds:ecx+4,0A;b=10004010A0 8B55 FC mov edx,dword ptr ss:ebp-4004010A3 C742 08 0200
10、0000 mov dword ptr ds:edx+8,2;c=2004010AA 8B45 FC mov eax,dword ptr ss:ebp-4004010AD C640 0C 61 mov byte ptr ds:eax+C,61;d='a'004010B1 8B45 FC mov eax,dword ptr ss:ebp-4 我们可以清楚的看到,初始化列表的初始化并不是按照代码的书写顺序先初始化a再初始化c,而是无序的初始化数据成员,这种顺序是无法通过c+进行调整的。/构造函数初始化有序进行class testint a;const int b;int c;char
11、 d;public:test():b(10)d=0x61;c=a=1;int main()test hacker;return 0; 构造函数用来初始化,一定会按照预先设定的代码执行,因此构造函数的初始化顺序是有序进行的。主要的反汇编代码如下:0040108A 894D FC mov dword ptr ss:ebp-4,ecx0040108D 8B45 FC mov eax,dword ptr ss:ebp-400401090 C740 04 0A000000 mov dword ptr ds:eax+4,0A;将const变量初始化00401097 8B4D FC mov ecx,dwor
12、d ptr ss:ebp-40040109A C641 0C 61 mov byte ptr ds:ecx+C,61;d='a'0040109E 8B55 FC mov edx,dword ptr ss:ebp-4004010A1 C702 01000000 mov dword ptr ds:edx,1;a=1;004010A7 8B45 FC mov eax,dword ptr ss:ebp-4004010AA C740 08 01000000 mov dword ptr ds:eax+8,1;c=1004010B1 8B45 FC mov eax,dword ptr ss:
13、ebp-4 对比不难发现,先执行初始化列表,然后执行构造函数,因此初始化列表是在构造函数之前执行的。 二:this指针到底在做什么 this指针在诸多的C+书中都会有类似的描述“始终指向当前正在调用的对象”,那么它到底指向什么呢?答案是它有ecx保存,始终指向当前对象的第一个数据成员。2.1:C+中变量内存分布 C+中的变量或者称为对象在内存的分布是不一样的,一方面作用域与存储类rase(register auto static extern)决定了变量分布于内存的堆还是栈,另一方面,先给谁分配内存的顺序也是有区别的。大致的规则如下A:单一变量,先声明先的变量,先分配内存 如下代码int ma
14、in() int a; int b; 会先分配a变量需要的内在,然后分配b变量需要的内存,因此,a的地址是最大的,即为ebp-4,而b的地是小的ebp-8。B:多变量的集合,比如数组、struct结构体、自定义的对象,先声明的变量后分配内存。如下代码:int main() int a3; a0的地址最小的,最后分配内存,a3的地址是最大的,先分配内存。 理解了上面的顺序我们再来看this指针到底做了什么?2.2:this指针到底在作什么 this指针始终指向当前对象的第一个数据成员,即告诉对象的行为(函数)要操作的地址是什么,用ecx寄存器保存其值。/理解this指针.class testpu
15、blic:int a;int b;int c;test()a=b=c; int main()unsigned thisaddr;test hacker;_asm/取this指针mov dword ptr ss:ebp-4,ecxcout<<&hacker.a<<""<<&hacker.b<<""<<&hacker.c<<""<<hex<<thisaddr<<endl;return 0; 执行结果如图6所示
16、,a的地址与this指针的值是一样的,即this指针始终指向对象的第一个数据成员的内存地址。当然这在继承中略有不同,这点不同在于vbtable的引入。三:虚基类继承时发生了什么 继承是什么呢?继承解实际上抽象了具有相同特性的类,这使得类之间有了一定的层次,最上层的是最核心的,即所有的类的交集,比如C+的输入输出类即为此种层次结构。如图7描述了C+的继承关系, 如图所示使用虚基类的情况是一个平行四边形的继承关系时,暂时称为平行四边形法则。/虚基类示例.class basepublic:int a;class d1:virtual public basepublic:int b;class d2:
17、virtual public basepublic:int c;class d3:public d1,public d2public:int sum;int main()d3 ob3;ob3.a=10;ob3.b=20;ob3.c=30;ob3.sum=ob3.a+ob3.b+ob3.c;cout<<ob3.sum<<endl;return 0; 3.1:vbtable(虚基类表)是如何工作的 将上面的程序载入OD,跟进主函数,关键代码如下:/反汇编代码00401598 6A 01 push 1 ;这个参数是一个控制标志0040159A 8D4D E8 lea ecx,
18、dword ptr ss:ebp-18 ; this指针指向d1的vbtable地址,即等同于第一个元素a的内存地址0040159D E8 FEFAFFFF call api.004010A0 ; 构造函数依次调用基类子类的构造函数004015A2 8B45 E8 mov eax,dword ptr ss:ebp-18 ; d1 vbtable004015A5 8B48 04 mov ecx,dword ptr ds:eax+4 ; eax+4=offset004015A8 C7440D E8 0A00000>mov dword ptr ss:ebp+ecx-18,0A ; this+o
19、ffset 此时我们执行完构造函数,观察栈区如图8所示,此时构造函数将虚基类的vbtable压入栈中,我们dd vbtable的内容,如图9所示。综合分析,得出vbtable的结构如下:vbtable strucx1dw?offset dw?x2 dw?vbtable ends 其中offset指明了当前数据成员距离惟一副本的偏移,在此程序中即为d1中vbtable所在栈地址与其数据成员a的偏移地址,即this虚基类通过vbtable中偏移+4的偏移量来定位多副本程序,从而实现子类中只有一个虚基类的副本。3.2:vbtable是如何引入的 重新载入程序,在40159d处跟进构造函数,会发现如下
20、代码:00401620 > 55 push ebp ; 保存基址00401621 8BE mov ebp,esp ;开辟新栈帧00401623 83EC 44 sub esp,44 ; 为新栈帧分配相应的内存00401626 53 push ebx00401627 56 push esi00401628 57 push edi00401629 51 push ecx ; 保存ecx,因为ecx要作为初始化的计数器0040162A 8D7D BC lea edi,dword ptr ss:ebp-440040162D B9 11000000 mov ecx,1100401632 B8 CC
21、CCCCCC mov eax,CCCCCCCC00401637 F3:AB rep stos dword ptr es:edi00401639 59 pop ecx ; this指针出栈0040163A 894D FC mov dword ptr ss:ebp-4,ecx ; 保存ecx0040163D 837D 08 00 cmp dword ptr ss:ebp+8,0 ; 参数1与0比较以判断是否使用虚基类00401641 74 13 je short api.0040165600401643 8B45 FC mov eax,dword ptr ss:ebp-400401646 C700
22、 28E04600 mov dword ptr ds:eax,offset api.d3:vbtable' ; d2的vbtable0040164C 8B4D FC mov ecx,dword ptr ss:ebp-40040164F C741 08 1CE04600 mov dword ptr ds:ecx+8,offset api.d3:vbtable' ; d1的vbtable 分析40163d处的代码,为cmp dword ptr ss:ebp+8,0,对于任何一个函数来说,它的栈区结构应该如图10所示,而此时的ebp+8即为参数1,因此在使用虚基类时,总会先push
23、1用于控制判断是否有虚基类,将将虚基类的vbtable保存到其多副本数据成员区,在本程序中即为d1和d2数据成员的a位置处。 综上所述,当使用虚基类时,构造函数会通过一个标志控制参数,将vbtable的地址保存到多副本数据成员的内存位置,对应于本程序的变量a,当访问具有多副本变量时,通过vbtable+4处的偏移值+this指针即可,这保证了继承时子类中只有基类的一个数据成员副本。 四:多态到底为何物 实际上C+面向对象程序设计的重要的问题是解决类与类之间的关系,类与类之间的关系,可以简单的理解为完全不同的类、大部分不同的类、大部分相同的类、完全相同的类。我们可以简称为类的四象,如图11所示,
24、结合太极四象更容易理解类与类的关系。多态即为只有一小部分相似的类的关系,反过来说即为大部分不同的类关系,这种关系通过运行时的vftable来完成。/示例class testpublic:virtual void vfunc()cout<<"base's vfunc"<<endl;class d1:public testpublic:void vfunc()cout<<"d1's vfunc!"<<endl;class d2:public testpublic:void vfunc()cout
25、<<"d2's vfunc!"<<endl;int main()test a,*p;d1 b;d2 c;p=&a;p->vfunc();p=&b;p->vfunc();p=&c;p->vfunc();return 0; 将上面的函数反汇编一下。/反汇编上面的程序分析虚函数机制004012C8 8D4D FC lea ecx,dword ptr ss:ebp-4 ; this指针 a004012CB E8 6CFDFFFF call api.0040103C004012D0 8D4D F4 lea ecx,dword ptr ss:ebp-C ; this指针 b004012D3 E8 69FDFFFF call api.00401041004012D8 8D4D F0 lea ecx,dword ptr ss:ebp-10 ; this指针 c004012DB E8 48FDFFFF call api.00401028
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 校园外语交流合作合同(2篇)
- 2025年是否可以随时解除委托合同
- A-V综合征的临床护理
- 新质生产力宣讲稿
- 新质生产力形式
- 眼部猪囊尾蚴病的临床护理
- 2025委托合同简单加工协议书格式
- 2025年社会工作者之中级社会综合能力模考模拟试题(全优)
- 腹直肌自发性破裂的临床护理
- 2025临时施工设施租赁合同范本
- 电商仓储外包合同协议
- 近三年小升初试卷及答案
- 美容学徒聘请协议书
- 江苏连云港市金灌投资发展集团有限公司、灌南城市发展集团有限公司等招聘笔试题库2025
- 四川宜宾环球集团有限公司招聘笔试真题2024
- 精神科护理目标管理
- 矩阵运算的新视角
- 人教版小学数学二年级下册期中综合素养测评A卷(1-5单元)(含答案)
- 肠外营养中电解质补充中国专家共识(2024版)解读
- 第六单元 有余数的除法测试卷(含答案)2024-2025学年二年级数学下册人教版
- 2024年福建泉州文旅集团招聘考试真题
评论
0/150
提交评论