Getting allocation address of an object in Delphi 7
I have the following code sequence:
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
d开发者_Python百科ummy := dummies[ iter ];
dummies[ iter ] := nil;
FreeAndNil( dummy );
end;
// Release the tracked object.
FreeAndNil( a );
end.
The output of the code is:
- TA instance allocated at 0x0012FF88
- TA size is 4012 Preparing to destroy
- TA instance allocated at 0x0012FF80
- TA size is 4012
I do not understand the "self" difference. Can you give me a hint? I would have expected the printed values to be the same.
Self
in an instance method is an implicit argument and is a reference to the instance that received the method call. It is implemented as a pointer.
The Addr
standard procedure is the same as the @
operator; it takes the address of the location passed to it. When you apply Addr
to Self
, you are getting the address of the parameter location, not the address of the instance. This parameter location is allocated on the stack, as it only needs to exist while the method call is active; when the method call returns, there is no longer any need for the space for the Self
parameter; it gets released, implicitly, by the CPU's stack pointer moving back to whatever it was before the method was called.
The reason why the Self
parameter may be at different locations in different invocations of methods on the same instance is because there may be more or less data on the stack at the time of the call. For example, the Self
parameter will be allocated differently if the method call sequence looks like A -> B -> C
versus A -> C
, because the stack frame for B
needs to be stored in between the stack frames for A
and C
. The stack frame is where the parameters and locals associated with any given invocation of a method are allocated.
The following code will give you an explanation:
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;
Here is the output:
AfterConstruction=10731904
AfterConstruction=1245020
BeforeDestruction=10731904
BeforeDestruction=1245028
So integer(self) is correct (both values equal 10731904), but integer(addr(self)) is not.
Because addr(self) don't show the self value, but where the self value is stored.
In both case, it's stored in the stack (use Alt-F2 to disassemble the prefix of both methods):
mov [esp],eax
So when you use addr(self), you're looking at the current stack address, not the self value.
Addr is not a simple typecast to a pointer but the same as @self, so using integer(self) is not the same as integer(addr(self)).
You should not use Addr(self) but self to find the allocation address of your object.
精彩评论