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
dummy := dummies[ iter ];
dummies[ iter ] := nil;
FreeAndNil( dummy );
end;
// Release the tracked object.
FreeAndNil( a );
end.
The output of the code is:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With