Delphi 中对象分配地址的获取

g9icjywg  于 2023-01-20  发布在  其他
关注(0)|答案(2)|浏览(312)

我有下面的代码序列:

program OverrideAfterConstructionEtc;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

type

TA = class( TInterfacedObject)
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
protected
FDummyData: array[ 1 .. 1000 ] of longint;
end;

{ TA }

procedure TA.AfterConstruction;
var
    selfPtr: Pointer;
    selfInt: Integer;
    selfStr: string;
    size: Integer;
begin
    inherited AfterConstruction;
    selfPtr := Addr( self );
    selfInt := Integer( selfPtr );
    selfStr := IntToHex( selfInt, 8 );

    size := TA.InstanceSize;
    WriteLn( 'TA instance allocated at 0x', selfStr );
    WriteLn( 'TA size is ', size );

end;

procedure TA.BeforeDestruction;
var
    selfPtr: Pointer;
    selfInt: Integer;
    selfStr: string;

    size: Integer;

begin

    selfPtr := Addr( self );
    selfInt := Integer( selfPtr );
    selfStr := IntToHex( selfInt, 8 );

    WriteLn( 'Preparing to destroy TA instance allocated at 0x', selfStr );

    size := TA.InstanceSize;
    WriteLn( 'TA size is ', size );

    inherited BeforeDestruction;
end;

const
    maxDummy = 1000;
var
    a: TA;
    dummy: TList;
    iter : integer;

    dummies: array [ 1 .. maxDummy ] of TList;
begin

    // Simulate a set of allocations.

    for iter := 1 to maxDummy do
    begin
        dummy := TList.Create;
        dummies[ iter ] := dummy;
    end;

    // Allocate the object we want to track.
    a := TA.Create;

    // Release the simulated allocations.
    for iter := 1 to maxDummy do
    begin
        dummy := dummies[ iter ];
        dummies[ iter ] := nil;
        FreeAndNil( dummy );
    end;


    // Release the tracked object.

    FreeAndNil( a );

end.

代码的输出为:

  • TA示例分配在0x0012FF88
  • TA大小为4012准备销毁
  • TA示例分配在0x0012FF80
  • TA大小为4012

我不明白"自我"的区别。你能给我一个提示吗?我本来期望打印的值是一样的。

xoefb8l8

xoefb8l81#

示例方法中的Self是一个隐式参数,是对接收方法调用的示例的引用。它被实现为指针。
Addr标准过程与@运算符相同;它获取传递给它的位置的地址。当你把Addr应用到Self时,你得到的是参数位置的地址,而不是示例的地址。这个参数位置在堆栈上分配,因为它只需要在方法调用活动时存在;当方法调用返回时,不再需要用于Self参数的空间;通过CPU的堆栈指针返回到调用该方法之前的位置,它被隐式地释放。
Self参数在同一示例上的不同方法调用中可能位于不同位置的原因是,调用时堆栈上的数据可能更多或更少。例如,如果方法调用序列看起来像A -> B -> CA -> C,则Self参数的分配将不同。因为B的堆栈帧需要存储在AC的堆栈帧之间。堆栈帧是分配与方法的任何给定调用相关联的参数和局部变量的地方。

koaltpgm

koaltpgm2#

下面的代码将给你一个解释:

type
  TA = class( TInterfacedObject)
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

procedure TA.AfterConstruction;
begin
  inherited;
  writeln('AfterConstruction=',integer(self));
  writeln('AfterConstruction=',integer(addr(self)));
end;

procedure TA.BeforeDestruction;
begin
  writeln('BeforeDestruction=',integer(self));
  writeln('BeforeDestruction=',integer(addr(self)));
  inherited;
end;

下面是输出:

AfterConstruction=10731904
AfterConstruction=1245020
BeforeDestruction=10731904
BeforeDestruction=1245028
    • 因此integer(self)正确**(两个值都等于10731904),但integer(addr(self))不正确。

因为addr(self)没有显示self的值,而是显示了self值的存储位置。
在这两种情况下,它都存储在堆栈中(使用Alt-F2反汇编两个方法的前缀):

mov [esp],eax

所以当你使用addr(self)的时候,你看到的是当前的堆栈地址,而不是self的值。
Addr不是简单的指针类型转换,而是和@self一样,所以使用integer(self)和使用integer(addr(self))不一样。
不应该使用Addr(self)而应该使用self来查找对象的分配地址。

相关问题