我很困惑:为什么显式地调用Delphi构造函数/作为普通方法不会创建新实例/为什么没有内存泄漏?
以下是一些示例代码:
TMyHelperClass = class(TObject)
private
fSomeHelperInt: integer;
public
property SomeHelperInt : integer read fSomeHelperInt write fSomeHelperInt;
constructor Create (const initSomeHelperInt : integer);
end;
TMyClass = class(TObject)
private
fHelper : TMyHelperClass;
public
constructor Create(const initSomeInt: integer);
destructor Destroy; override;
property Helper : TMyHelperClass read fHelper;
end;
实现:
constructor TMyHelperClass.Create(const initSomeHelperInt: integer);
begin
fSomeHelperInt := initSomeHelperInt;
end;
constructor TMyClass.Create(const initSomeInt: integer);
begin
fHelper := TMyHelperClass.Create(initSomeInt);
end;
destructor TMyClass.Destroy;
begin
fHelper.Free;
inherited;
end;
用法:
var
my : TMyClass;
begin
my := TMyClass.Create(2016);
try
//WHY is this ok to be used ?
my.Helper.Create(2017);
finally
my.Free;
end;
end;
为什么我可以将TMyHelperClass +的Create构造函数称为普通方法?我的意思是 - 这正是我想要的 - 但是怎么没有问题(有记忆)?
我想答案是因为没有调用Create方法像TMyHelperClass.Create(创建TMyHelperClass的实例)?
这种调用构造函数的方式是否可以使用普通方法/ ok?
答案 0 :(得分:10)
是的,您可以将构造函数作为普通方法调用 这样做是不好的做法。
为什么没有内存泄漏?
来自:http://docwiki.embarcadero.com/RADStudio/Seattle/en/Methods#Constructors
使用对象引用(而不是对象引用)调用构造函数时 类引用),它不会创建对象。相反, 构造函数对指定的对象进行操作,仅执行 构造函数的实现中的语句,然后返回一个 引用该对象。通常在一个构造函数上调用 对象引用与继承的保留字一起使用 执行继承的构造函数。
在调用TObject.Create
(类引用) vs AObject.Create
(实例引用)时,编译器将生成不同的代码。
反模式警告
滥用构造函数作为常规方法将导致分配资源时出现问题
通常构造函数和析构函数是匹配的,但是如果你调用构造函数两次(就像调用实例的构造函数一样),你将分配资源两次,但只释放一次。
如果要将构造函数的主体作为普通方法调用,请创建一个新方法并在构造函数中调用该方法。
E.g:
constructor TTest.Create;
begin
inherited;
//Allocate needed resources here.
Init;
end;
procedure TTest.Init;
begin
//Do initialization stuff
//But don't allocate any resources
现在,您可以在需要时安全地调用init
方法,而不会发生任何意外事故。
可以在没有“构造”任何东西的情况下调用构造函数的原因是以下代码必须工作:
constructor TTest.Create(const AObject: TObject);
begin //constructor of TTest happens here
inherited Create; //instance call to TParent.Create;
//Other code
end;
在极少数情况下,您可以完全跳过对构造函数的类调用 以下代码将起作用:
MyTest:= TTest.NewInstance;
//Voodoo code here.
MyTest.Create;
这可用于防止编译器插入在TTest.Create
的调用中生成的自动代码。
这很少需要,在20年的编码中,我只使用过一次
这个用例是速度敏感代码,我想避免异常检查的开销和归零分配的空间,这是在Delphi支持带方法的记录之前。
如果我必须再次执行该代码,我将使用一条记录,并在堆上分配一个包含1000条记录的池,并根据需要将其移出。