[Delphi]Delphi - 对象构造浅析后续

转载:http://www.cnblogs.com/huangjacky/archive/2009/12/08/1619438.html

技术交流,DH讲解.

之前一篇文章已经讲过对象构造的过程,但是我们那个对象无任何东西,这里我们在已有的基础上面加点儿东西再来看看.
代码改成:

01   THuangJacky = class
02   private
03     FName:string;
04   public
05     procedure SayMyName();
06     constructor Create();
07   end;
08  
09 var
10   Form3: TForm3;
11  
12 implementation
13  
14 {$R *.dfm}
15  
16 procedure TForm3.btn1Click(Sender: TObject);
17 var
18   A:THuangJacky;
19 begin
20   A:=THuangJacky.Create;
21   A.Free;
22 end;
23  
24 { THuangJacky }
25  
26 constructor THuangJacky.Create;
27 begin
28   FName:='HuangJacky';
29 end;
30  
31 procedure THuangJacky.SayMyName;
32 begin
33   ShowMessage(FName);
34 end;

我们可以看到多了一个私有域和公有方法.
构造过程:

1 utMain.pas.38: A:=THuangJacky.Create;
2 004B33E4 B201             mov dl,$01
3 004B33E6 A1C8324B00       mov eax,[$004b32c8] //类地址都变化了.
4 004B33EB E808000000       call THuangJacky.Create<br><hr>
01 utMain.pas.45: begin
02 004B33F8 53               push ebx
03 004B33F9 56               push esi
04 004B33FA 84D2             test dl,dl
05 004B33FC 7408             jz $004b3406
06 004B33FE 83C4F0           add esp,-$10
07 004B3401 E8D61EF5FF       call @ClassCreate //前面都一样,又是调用这个参数,但是我们知道eax参数(类地址)变化了
08 004B3406 8BDA             mov ebx,edx
09 004B3408 8BF0             mov esi,eax
10 utMain.pas.46: FName:='HuangJacky';
11 004B340A 8D4604           lea eax,[esi+$04]
12 004B340D BA40344B00       mov edx,$004b3440
13 004B3412 E82D34F5FF       call @UStrAsg

我们可以看到是先构造然后赋值字符串的,@ClassCreate之前的判断都一样的,我们看看$004b3440 地址的数据:
image 不要忘了我是在D2010下面,所以是Unicode字符串.


01 @ClassCreate:
02 004052DC 52               push edx
03 004052DD 51               push ecx
04 004052DE 53               push ebx
05 004052DF 84D2             test dl,dl
06 004052E1 7C03             jl $004052e6
07 004052E3 FF50F4           call dword ptr [eax-$0c]
08 004052E6 31D2             xor edx,edx
09 004052E8 8D4C2410         lea ecx,[esp+$10]
10 004052EC 648B1A           mov ebx,fs:[edx]
11 004052EF 8919             mov [ecx],ebx
12 004052F1 896908           mov [ecx+$08],ebp
13 004052F4 C7410405534000   mov [ecx+$04],$00405305
14 004052FB 89410C           mov [ecx+$0c],eax
15 004052FE 64890A           mov fs:[edx],ecx
16 00405301 5B               pop ebx
17 00405302 59               pop ecx
18 00405303 5A               pop edx
19 00405304 C3               ret

这个函数还是一样的.我们不需要看了,从这里跳吧004052E3


01 TObject.NewInstance:
02 00404D40 53               push ebx
03 00404D41 8BD8             mov ebx,eax
04 00404D43 8BC3             mov eax,ebx
05 00404D45 E826000000       call TObject.InstanceSize
06 00404D4A E885F4FFFF       call @GetMem
07 00404D4F 8BD0             mov edx,eax
08 00404D51 8BC3             mov eax,ebx
09 00404D53 E85C000000       call TObject.InitInstance
10 00404D58 5B               pop ebx
11 00404D59 C3               ret
12 00404D5A 8BC0             mov eax,eax

还是到这里来了,貌似一路上什么都没有变.但是我推想TObject.InstanceSize返回值会有变化,毕竟多了一个私有成员,但是@GetMem肯定不变,TObject.InitInstance也会变化,毕竟有方法了.跟进验证


果然现在TObject.InstanceSize返回值是$0C了,也就是12,上一次什么都没有是8,现在多了4,多了一个指针地址,难道这个指针就是字符串指针一会儿验证下?
接下来我们直接进入InitInstance看看.有什么变化没有?
我跟进去发现没有变化,这个方法不在IntfTable里面的.我们看看初始化完成后的对象数据.
image
我们看看$004b3320是什么东西.
image
这里看上去像是THuangJacky的内存地址,那我们最先看见的[$004b32c8]是什么?我们注意地址加上[]就是地址指向的值.
image 是的,就是指向这里的.
我们让它Create完后再来看对象的数据,OK?


image
变化了,第二地址会是什么呢?
image 
果然是第一个成员变量的值.

 


有朋友要问那么SayMyName这个方法的信息存在哪里的呢?这个方法我们要知道是和类挂钩的,所以肯定在类的内存信息块里面的.我们到类信息块去看看,SayMyName字符串前面有4个字节$004B3464
image 

1 utMain.pas.52: ShowMessage(FName);
2 004B3464 8B4004           mov eax,[eax+$04]
3 004B3467 E8AC4CFBFF       call ShowMessage

最后来做个试验,我们给类里面再加一个私有成员,然后实现ClassHack,哈哈,违反面向对象的方法

01 THuangJacky = class
02   strict private //私有了,访问不了了
03     FName:string;
04     FAge:Integer;
05   public
06     procedure SayMyName();
07     procedure SayMyAge();
08     constructor Create();
09   end;
10  
11 var
12   Form3: TForm3;
13  
14 implementation
15  
16 {$R *.dfm}
17  
18 procedure TForm3.btn1Click(Sender: TObject);
19 var
20   A:THuangJacky;
21   Pi:PInteger;
22 begin
23   A:=THuangJacky.Create;
24   Pi:=PInteger(Integer(A)+8); //我就要Hack
25   ShowMessage(IntToStr(Pi^)); //12
26   A.SayMyAge; //12 上面是一样的
27   A.Free;
28 end;
29  
30 { THuangJacky }
31  
32 constructor THuangJacky.Create;
33 begin
34   FName:='HuangJacky';
35   FAge:=12;
36 end;
37  
38 procedure THuangJacky.SayMyAge;
39 begin
40   ShowMessage(IntToStr(FAge));
41 end;
42  
43 procedure THuangJacky.SayMyName;
44 begin
45   ShowMessage(FName);
46 end;

看下新对象的内存和类的内存:
对象的:

image 
类的:
image 
注意看方法前面的地址.哈哈.

今天就说到这里,或许有朋友问这样研究有什么意思.下一篇文章就讲单例模式,这个总有用吧,Delphi里面单例模式就要和我们讲的这些知识挂上钩了,因为Delphi很特别哟.

好,下次见,我是DH.

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏