我有以下代码序列:
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.
代码的输出是:
我不明白“自我”的区别。你能给我一个提示吗?我原本预计打印值会相同。
答案 0 :(得分:13)
Self
是隐式参数,是接收方法调用的实例的引用。它被实现为指针。
Addr
标准程序与@
运算符相同;它需要传递给它的位置的地址。当您将Addr
应用于Self
时,您将获得参数位置的地址,不实例的地址。此参数位置在堆栈上分配,因为它只需要在方法调用处于活动状态时存在;当方法调用返回时,不再需要Self
参数的空间;它被隐式释放,由CPU的堆栈指针移回到调用方法之前的状态。
Self
参数可能位于同一实例上的方法的不同调用中的不同位置的原因是因为在调用时堆栈上可能存在更多或更少的数据。例如,如果方法调用序列看起来像Self
而不是A -> B -> C
,则A -> C
参数将以不同方式分配,因为B
的堆栈帧需要存储在堆叠A
和C
的框架。堆栈帧是分配与方法的任何给定调用相关联的参数和本地的位置。
答案 1 :(得分:10)
以下代码将为您提供解释:
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)不显示自身值,而是存储自身值的位置。
在这两种情况下,它都存储在堆栈中(使用Alt-F2来反汇编这两种方法的前缀):
mov [esp],eax
因此,当您使用addr(self)时,您正在查看当前堆栈地址,而不是自身值。
Addr不是指针的简单类型转换,但与@self相同,因此使用整数(self)与整数(addr(self))不同。
你不应该使用Addr(self)而是self来查找对象的分配地址。