在Delphi 7 a中,对象的创建是这样的:

A := TTest.Create;
try
  ...
finally
  A.Free;
end;

然而,马可·坎图(MarcoCantù)在一张经文中说,他们在Embercadero
A1 := nil;
A2 := nil;
try
  A1 := TTest.Create;
  A2 := TTest.Create;
  ...
finally
  A2.Free;
  A1.Free;
end;

在版本升级期间,尝试“最终阻止”的逻辑是否发生了变化?第二个例子对我来说似乎是一个典型的错误!

最佳答案

两者都是可接受的模式。这是,不是,但已更改。

首先,让我们介绍一下您所熟悉的内容以及为什么它是正确的。

{ Note that here as a local variable, A may be non-nil, but
  still not refer to a valid object. }
A := TTest.Create;
try
  { Enter try/finally if and only if Create succeeds. }
finally
  { We are guaranteed that A was created. }
  A.Free;
end;

在上面的代码中:如果在之后分配了A,请尝试,那么Create可能会失败并跳到此处。这将尝试从内存中未定义的位置释放对象。它可能导致访问冲突或行为不稳定。请注意,编译器还会发出警告:A.Free;可能未初始化A。这是因为由于构造函数中的异常,在分配 A之前,有可能跳转到finally块

那么为什么Marco的代码可以接受?
A1 := nil; { Guarantees A1 initialised *before* try }
A2 := nil; { Guarantees A2 initialised *before* try }
try
  A1 := TTest.Create;
  A2 := TTest.Create;
  ...
finally
  { If either Create fails, A2 is guaranteed to be nil.
    And Free is safe from a nil reference. }
  A2.Free;
  { Similarly, if A1's Create fails, Free is still safe.
    And if A1's create succeeds, but A2's fails: A1 refers to a valid
    object and can be destroyed. }
  A1.Free;
end;

请注意,Marco的代码依赖于Free()行为的一些细微差别。有关更多信息,请参见以下问答:
  • Shouldn't calling Free on an object reference set to nil throw an Access Violation every time it is called?
  • Why should I not use "if Assigned()" before using or freeing things?


  • 该技术背后的目的是避免嵌套的try..finally块可能会变得凌乱。例如。
    A1 := TTest.Create;
    try
      A2 := TTest.Create;
      try
        {...}
      finally
        A2.Free;
      end;
    finally
      A1.Free;
    end;
    

    Marco的代码降低了嵌套级别,但是需要对本地引用进行“预初始化”。

    维多利亚提出了一个警告,即如果A2的析构函数在Marco的代码中失败,那么A1将不会被释放。这将是一定的内存泄漏。但是,我认为任何析构函数一旦失败:
  • 它没有成功完成;
  • 因此可能已经至少泄漏了内存或资源。
  • 以及您的系统的整体完整性也令人怀疑。如果“简单清理”失败了:为什么,出了什么问题,这将导致什么 future 的问题?

  • 因此,我能提供的最佳建议是:小心确保析构函数的正确性。

    09-15 20:27