• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Delphi-对象构造浅析

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

自己用上了D2010,然后心中一直想着一句话就是Borland的编译器比其他的都要好很多,虽然现在Delphi已经易主了.
我们现在一天都在说面向对象,但是我们知道对象在内存都是一堆数据而已,那么Delphi编译器是怎么来管理这些数据的呢?
抱着这样的态度,我用简单的代码进行了一些测试,当然技术有限,有所错误,希望朋友指出来不要喷我.
本文首发http://huangjacky.cnblogs.com/,转载请注明,谢谢.
好开始,本文是以D2010为基础的.其他版本上面可能会有所不同.

 

代码
  1 编译器创建对象的过程:
  2  假设有这样一个类:
  3  THuangJacky= class
  4 
  5   end;
  6   
  7   我们创建一个对象:
  8   procedure TForm3.btn1Click(Sender: TObject);
  9  var
 10    A:THuangJacky;
 11  begin
 12    A:=THuangJacky.Create;
 13    A.Free;
 14  end
 15  
 16 看看编译器都在干什么?
 17 Unit3.pas.34: A:=THuangJacky.Create;
 18 //这个Dl为什么要设置成1?
 19 004A3BE0 B201             mov dl,$01
 20 //$004a3b54这个就是THuangJacky类的地址
 21 004A3BE2 A1543B4A00       mov eax,[$004a3b54]
 22 //由于我们没有在构造方法里面写其他代码所以这里就直接调用TObject.Create
 23 004A3BE7 E88C11F6FF       call TObject.Create
 24 ----------------------------------------------------------
 25 TObject.Create:
 26 00404D78 84D2             test dl,dl
 27 00404D7A 7408             jz $00404d84 //如果dl为0就跳--------------------|
 28 00404D7C 83C4F0           add esp,-$10 //分配堆栈空间给局部变量.            |
 29 00404D7F E840050000       call @ClassCreate //System.ClassCreate         |
 30 00404D84 84D2             test dl,dl                                   <-|   
 31 00404D86 740F             jz $00404d97 //为0 跳出构造函数-----------------------|
 32 00404D88 E88F050000       call @AfterConstruction                             |
 33 00404D8D 648F0500000000   pop dword ptr fs:[$00000000]//还原SEH                |
 34 00404D94 83C40C           add esp,$0c //堆栈平衡                               | 
 35 00404D97 C3               ret                                               <-| 
 36 ----------------------------------------------------------------------------------
 37 @ClassCreate:
 38 004052C4 52               push edx
 39 004052C5 51               push ecx
 40 004052C6 53               push ebx
 41 004052C7 84D2             test dl,dl
 42 004052C9 7C03             jl $004052ce //dl小于0跳----------------------|
 43 004052CB FF50F4           call dword ptr [eax-$0c]//这个是NewInstance   |
 44 004052CE 31D2             xor edx,edx //清空edx=0                     <-|
 45 004052D0 8D4C2410         lea ecx,[esp+$10]
 46 004052D4 648B1A           mov ebx,fs:[edx] //这里应该是SEH的操作,和构造无关
 47 004052D7 8919             mov [ecx],ebx
 48 004052D9 896908           mov [ecx+$08],ebp
 49 004052DC C74104ED524000   mov [ecx+$04],$004052ed
 50 004052E3 89410C           mov [ecx+$0c],eax
 51 004052E6 64890A           mov fs:[edx],ecx
 52 004052E9 5B               pop ebx
 53 004052EA 59               pop ecx
 54 004052EB 5A               pop edx
 55 004052EC C3               ret 
 56 004052ED E9E2010000       jmp @HandleAnyException //如果发生异常那么就会调用到这里
 57 004052F2 8B44242C         mov eax,[esp+$2c]
 58 004052F6 8B400C           mov eax,[eax+$0c]
 59 004052F9 85C0             test eax,eax
 60 004052FB 740E             jz $0040530b
 61 004052FD 8B08             mov ecx,[eax]
 62 004052FF B281             mov dl,$81
 63 00405301 50               push eax
 64 00405302 FF51FC           call dword ptr [ecx-$04]
 65 00405305 58               pop eax
 66 00405306 E809000000       call @ClassDestroy
 67 0040530B E8C8050000       call @RaiseAgain
 68 00405310 C3               ret 
 69 00405311 8D4000           lea eax,[eax+$00]
 70 -----------------------------------------------------------------
 71 TObject.NewInstance:
 72 00404D40 53               push ebx
 73 00404D41 8BD8             mov ebx,eax //在ebx中保存THuangJacky类指针
 74 00404D43 8BC3             mov eax,ebx //因为InstanceSize返回值会覆盖eax
 75 00404D45 E826000000       call TObject.InstanceSize
 76 00404D4A E885F4FFFF       call @GetMem //分配内存
 77 00404D4F 8BD0             mov edx,eax  //edx=$00eb0ec0,分配的内存块
 78 00404D51 8BC3             mov eax,ebx  //eax=$004a3b54,类指针
 79 00404D53 E85C000000       call TObject.InitInstance
 80 00404D58 5B               pop ebx
 81 00404D59 C3               ret 
 82 00404D5A 8BC0             mov eax,eax
 83 ------------------------------------------------------------------
 84 TObject.InstanceSize的pascal形式:
 85 class function TObject.InstanceSize: Longint;
 86 begin
 87   Result := PInteger(Integer(Self) + vmtInstanceSize)^;
 88 end;
 89 就是类指针-52的地址,类指针是多少?刚才赋值给Eax的那个值$004a3b54
 90 汇编代码形式:
 91 TObject.InstanceSize:
 92 00404D70 83C0CC           add eax,-$34   //减去52
 93 00404D73 8B00             mov eax,[eax]  //指向的值传给eax返回,可以看到是8
 94 00404D75 C3               ret            //退出
 95 00404D76 8BC0             mov eax,eax
 96 -----------------------------------------------------------------
 97 接下来该分配内存了
 98 @GetMem:
 99 004041D4 85C0             test eax,eax  
100 004041D6 7E13             jle $004041eb //eax小于等于0------------------|
101 004041D8 FF1560A74A00     call dword ptr [$004aa760] //调用SysGetMem   |
102 004041DE 85C0             test eax,eax //eax=00eb0ec0                  |
103 004041E0 7402             jz $004041e4  //等于0跳,跳不了了----|          |
104 004041E2 F3C3             rep ret                           |          |
105 004041E4 B001             mov al,$01                      <-|          |
106 004041E6 E945010000       jmp Error                                    |
107 004041EB 31C0             xor eax,eax //返回就是空了                 <-|
108 004041ED F3C3             rep ret 
109 004041EF 90               nop 
110 -------------------------------------------------------------------
111 //这个就是实际分配内存的函数,太长了.
112 //类指针是$004a3b54
113 SysGetMem:
114 00402CC8 8D5003           lea edx,[eax+$03//edx=11
115 00402CCB C1EA03           shr edx,$03 //edx右移3位,edx=1
116 00402CCE 3D2C0A0000       cmp eax,$00000a2c //eax=8,肯定比$0a2c小
117 00402CD3 53               push ebx //ebx=$004a3bac,偏移是$58(88),查表 是vmtSelfPtr指向虚方法表的指针 
118 00402CD4 8A0D51D04A00     mov cl,[$004ad051] //cl=00,因为小头地址
119 00402CDA 0F8748020000     jnbe $00402f28 //如果eax不小于等于$0a2c,跳不了----------------------------------------
120 00402CE0 84C9             test cl,cl
121 00402CE2 0FB682E0D84A00   movzx eax,[edx+$004ad8e0] //eax=0
122 00402CE9 8D1CC580A04A00   lea ebx,[eax*8+$4aa080]   //ebx=$004aa080
123 00402CF0 7556             jnz $00402d48 //cl不等于0,跳不了---------------------------------------
124 00402CF2 8B5304           mov edx,[ebx+$04//edx=$00eb0ce0
125 00402CF5 8B4208           mov eax,[edx+$08//eax=$00eb0ec0 后面一个Word感觉有猫腻
126 00402CF8 B9F8FFFFFF       mov ecx,$fffffff8 //ecx=$fffffff8
127 00402CFD 39DA             cmp edx,ebx //比较ebx和edx,等于0跳
128 00402CFF 7417             jz $00402d18 //跳不了
129 00402D01 83420C01         add dword ptr [edx+$0c],$01//将edx+$0c地址上面的数+1,位$0000001D
130 00402D05 2348FC           and ecx,[eax-$04//ecx与上eax-$4地址上的数(00000001),ecx=0
131 00402D08 894A08           mov [edx+$08],ecx //edx+$08地址上数为0
132 00402D0B 8950FC           mov [eax-$04],edx //$00EB0EBC->$00eb0ce0
133 00402D0E 7428             jz $00402d38 //ecx=0 所以这里跳走----|
134 00402D10 C60300           mov byte ptr [ebx],$00             |
135 00402D13 5B               pop ebx                            |
136 00402D14 C3               ret                                |
137 00402D15 90               nop                                |
138 00402D16 90               nop                                |
139 00402D17 90               nop                                |
140 00402D18 8B5310           mov edx,[ebx+$10]                  |
141 00402D1B 0FB74B02         movzx ecx,[ebx+$02]                |
142 00402D1F 01C1             add ecx,eax                        |
143 00402D21 3B430C           cmp eax,[ebx+$0c]                  |
144 00402D24 7776             jnbe $00402d9c                     |
145 00402D26 83420C01         add dword ptr [edx+$0c],$01        |
146 00402D2A 894B08           mov [ebx+$08],ecx                  |
147 00402D2D C60300           mov byte ptr [ebx],$00             |
148 00402D30 8950FC           mov [eax-$04],edx                  |
149 00402D33 5B               pop ebx                            |
150 00402D34 C3               ret                                |
151 00402D35 90               nop                                | 
152 00402D36 90               nop                                |
153 00402D37 90               nop                                |
154 00402D38 8B4A04           mov ecx,[edx+$04//ecx=$004aa080<-|  
155 00402D3B 895914           mov [ecx+$14],ebx //[$004aa094]->$004aa080
156 00402D3E 894B04           mov [ebx+$04],ecx //[$004aa084]->$004aa080 这一块内存都成了它了
157 00402D41 C60300           mov byte ptr [ebx],$00 //这个地址本来就是0
158 00402D44 5B               pop ebx //弹出对象指针.
159 00402D45 C3               ret //返回了,剩下的代码就省略了
160 ---------------------------------------------------------------------
161 TObject.InitInstance:
162 Pascal版本:
163 class function TObject.InitInstance(Instance: Pointer): TObject;
164 var
165   IntfTable: PInterfaceTable;
166   ClassPtr: TClass;
167   I: Integer;
168 begin
169  //1 首先是将分配对象空间,清0
170   FillChar(Instance^, InstanceSize, 0);
171   //2 将实例与类关联起来
172   PInteger(Instance)^ := Integer(Self);
173   //
174   ClassPtr := Self;
175   //下面的代码是将实例的接口表指向HuangJacky类以及父类的
176   //相关结构体的定义:
177   {
178     PInterfaceEntry = ^TInterfaceEntry;
179    TInterfaceEntry = packed record
180      IID: TGUID;
181      VTable: Pointer;//VMT?
182      IOffset: Integer;
183      ImplGetter: Integer;
184    end;
185  
186    PInterfaceTable = ^TInterfaceTable;
187    TInterfaceTable = packed record
188      EntryCount: Integer;
189      Entries: array[0..9999] of TInterfaceEntry;
190    end;
191  }
192   while ClassPtr <> nil do
193   begin
194     IntfTable := ClassPtr.GetInterfaceTable;
195     if IntfTable <> nil then
196       for I := 0 to IntfTable.EntryCount-1 do
197      with IntfTable.Entries[I] do
198      begin
199        if VTable <> nil then
200          PInteger(@PAnsiChar(Instance)[IOffset])^ := Integer(VTable);
201      end;
202     ClassPtr := ClassPtr.ClassParent;
203   end;
204   Result := Instance;
205 end;
206 理解了我们来看
207 汇编版本:
208 00404DB4 53               push ebx //$004A3BAC,vmtSelfPtr
209 00404DB5 56               push esi
210 00404DB6 57               push edi //这3个push都是为了保护现场
211 00404DB7 89C3             mov ebx,eax //ebx=eax=$004a3bac
212 00404DB9 89D7             mov edi,edx //edi=edx=$00eb0ec0
213 00404DBB AB               stosd //将eax移到edi中去,一次移动一个DWORD,隐含add edi,4
214 00404DBC 8B4BCC           mov ecx,[ebx-$34//ecx=8,InstanceSize.

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
{matlab}取二值图像centroid几种方法性能比较 - Lvpengms发布时间:2022-07-18
下一篇:
MATLAB,从Figure图中提取数据发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap