将Delphi构造函数称为普通方法 - 任何隐藏的副作用?

Tor*_*edo 2 delphi constructor

我很困惑:为什么显式调用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;
Run Code Online (Sandbox Code Playgroud)

执行:

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;
Run Code Online (Sandbox Code Playgroud)

用法:

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;
Run Code Online (Sandbox Code Playgroud)

为什么我可以将TMyHelperClass +的Create构造函数称为普通方法?我的意思是 - 这正是我想要的 - 但是怎么没有问题(有记忆)?

我想答案是因为Create方法没有像TMyHelperClass.Create那样被调用(用于创建TMyHelperClass的实例)?

这种调用构造函数的方式是否可以使用普通方法/ ok?

Joh*_*ica 10

是的,您可以将构造函数作为普通方法调用.
这样做是不好的做法.

为什么没有内存泄漏?

来自:http://docwiki.embarcadero.com/RADStudio/Seattle/en/Methods#Constructors

使用对象引用(而不是类引用)调用构造函数时,它不会创建对象.相反,构造函数对指定的对象进行操作,仅执行构造函数实现中的语句,然后返回对该对象的引用.通常在对象引用上调用构造函数,并结合继承的保留字来执行继承的构造函数.

调用TObject.Create (类引用) vs AObject.Create (实例引用)时,编译器将生成不同的代码.

反模式警告
滥用构造函数作为常规方法将导致分配资源时出现问题.
通常构造函数和析构函数是匹配的,但是如果你调用构造函数两次(就像调用实例的构造函数时一样),你将分配资源两次,但只释放一次.

如果要将构造函数的主体作为普通方法调用,请创建一个新方法并在构造函数中调用该方法.

例如:

constructor TTest.Create;
begin
  inherited;
  //Allocate needed resources here.
  Init;
end;

procedure TTest.Init;
begin
  //Do initialization stuff
  //But don't allocate any resources
Run Code Online (Sandbox Code Playgroud)

现在,您可以init在需要时安全地调用该方法,而不会发生任何意外.

可以在没有"构造"任何东西的情况下调用构造函数的原因是以下代码必须工作:

constructor TTest.Create(const AObject: TObject);
begin                //constructor of TTest happens here
  inherited Create;  //instance call to TParent.Create;
  //Other code
end;
Run Code Online (Sandbox Code Playgroud)

在极少数情况下,您可以完全跳过对构造函数的类调用.
以下代码将起作用:

MyTest:= TTest.NewInstance;
//Voodoo code here.
MyTest.Create;
Run Code Online (Sandbox Code Playgroud)

这可以用于防止编译器插入在调用中生成的自动代码TTest.Create.
这很少需要,在20年的编码中我只使用过一次.
这个用例是速度敏感代码,我想避免异常检查的开销和归零分配的空间,这是在Delphi支持带方法的记录之前.

如果我必须再次执行该代码,我将使用一条记录,并在堆上分配一个包含1000条记录的池,并根据需要将其移出.