Получение адреса размещения объекта в Delphi 7 - PullRequest
4 голосов
/ 12 января 2011

У меня следующая кодовая последовательность:

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

Я не понимаю разницы между собой. Можете ли вы дать мне подсказку? Я ожидал, что напечатанные значения будут такими же.

Ответы [ 2 ]

13 голосов
/ 12 января 2011

Self в методе экземпляра является неявным аргументом и является ссылкой на экземпляр, который получил вызов метода.Он реализован в виде указателя.

Стандартная процедура Addr такая же, как у оператора @;это берет адрес местоположения, переданного ему.Когда вы применяете Addr к Self, вы получаете адрес местоположения параметра, не адрес экземпляра.Расположение этого параметра выделяется в стеке, так как он должен существовать только при активном вызове метода;когда вызов метода возвращается, больше нет необходимости в месте для параметра Self;он неявно освобождается указателем стека ЦП, возвращающимся к тому, что было до вызова метода.

Причина, по которой параметр Self может находиться в разных местах в разных вызовах методов одного и того жеЭто связано с тем, что во время вызова в стеке может быть больше или меньше данных.Например, параметр Self будет выделен по-разному, если последовательность вызова метода выглядит как A -> B -> C против A -> C, поскольку кадр стека для B необходимо хранить между кадрами стека для A и C.Кадр стека - это место, где размещаются параметры и локальные объекты, связанные с любым заданным вызовом метода.

10 голосов
/ 12 января 2011

Следующий код даст вам объяснение:

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

Таким образом, целое число (self) является правильным (оба значения равны 10731904), а целое число (addr (self)) - нет.

Потому что addr (self) показывает не значение self, а место хранения значения self.

В обоих случаях он хранится в стеке (используйте Alt-F2, чтобы разобрать префикс обоих методов):

mov [esp],eax

Поэтому, когда вы используете addr (self), вы смотрите на текущий адрес стека, а не на значение self.

Addr - это не просто приведение типа к указателю, но то же самое, что и @self, поэтому использование integer (self) не совпадает с integer (addr (self)).

Вы не должны использовать Addr (self), но self, чтобы найти адрес выделения вашего объекта.

...