• Delphi 对象构造浅析后续


    技术交流,DH讲解.

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

      THuangJacky = class
      private
        FName:string;
      public
        procedure SayMyName();
        constructor Create();
      end;
    
    var
      Form3: TForm3;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm3.btn1Click(Sender: TObject);
    var
      A:THuangJacky;
    begin
      A:=THuangJacky.Create;
      A.Free;
    end;
    
    { THuangJacky }
    
    constructor THuangJacky.Create;
    begin
      FName:='HuangJacky';
    end;
    
    procedure THuangJacky.SayMyName;
    begin
      ShowMessage(FName);
    end;

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

    utMain.pas.38: A:=THuangJacky.Create;
    004B33E4 B201             mov dl,$01
    004B33E6 A1C8324B00       mov eax,[$004b32c8] //类地址都变化了.
    004B33EB E808000000       call THuangJacky.Create

    utMain.pas.45: begin
    004B33F8 53               push ebx
    004B33F9 56               push esi
    004B33FA 84D2             test dl,dl
    004B33FC 7408             jz $004b3406
    004B33FE 83C4F0           add esp,-$10
    004B3401 E8D61EF5FF       call @ClassCreate //前面都一样,又是调用这个参数,但是我们知道eax参数(类地址)变化了
    004B3406 8BDA             mov ebx,edx
    004B3408 8BF0             mov esi,eax
    utMain.pas.46: FName:='HuangJacky';
    004B340A 8D4604           lea eax,[esi+$04]
    004B340D BA40344B00       mov edx,$004b3440
    004B3412 E82D34F5FF       call @UStrAsg
    

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


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


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


    TObject.NewInstance:
    00404D40 53               push ebx
    00404D41 8BD8             mov ebx,eax
    00404D43 8BC3             mov eax,ebx
    00404D45 E826000000       call TObject.InstanceSize
    00404D4A E885F4FFFF       call @GetMem
    00404D4F 8BD0             mov edx,eax
    00404D51 8BC3             mov eax,ebx
    00404D53 E85C000000       call TObject.InitInstance
    00404D58 5B               pop ebx
    00404D59 C3               ret 
    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 

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

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

    THuangJacky = class
      strict private //私有了,访问不了了
        FName:string;
        FAge:Integer;
      public
        procedure SayMyName();
        procedure SayMyAge();
        constructor Create();
      end;
    
    var
      Form3: TForm3;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm3.btn1Click(Sender: TObject);
    var
      A:THuangJacky;
      Pi:PInteger;
    begin
      A:=THuangJacky.Create;
      Pi:=PInteger(Integer(A)+8); //我就要Hack
      ShowMessage(IntToStr(Pi^)); //12
      A.SayMyAge; //12 上面是一样的
      A.Free;
    end;
    
    { THuangJacky }
    
    constructor THuangJacky.Create;
    begin
      FName:='HuangJacky';
      FAge:=12;
    end;
    
    procedure THuangJacky.SayMyAge;
    begin
      ShowMessage(IntToStr(FAge));
    end;
    
    procedure THuangJacky.SayMyName;
    begin
      ShowMessage(FName);
    end;

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

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

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

    好,下次见,我是DH.

  • 相关阅读:
    Autofac ASP.NET Web API (Beta) Integration
    An Autofac Lifetime Primer
    Web api help page error CS0012: Type "System.Collections.Generic.Dictionary'2错误
    c++ 全局变量初始化的一点总结
    C++中extern关键字用法小结
    为什么多线程读写 shared_ptr 要加锁?
    CentOS7 安装Chrome
    在CentOS 7中使用VS Code编译调试C++项目
    am335x hid-multitouch.c
    implicit declaration of function 'copy_from_user'
  • 原文地址:https://www.cnblogs.com/huangjacky/p/1619438.html
Copyright © 2020-2023  润新知