在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()
行为的一些细微差别。有关更多信息,请参见以下问答:该技术背后的目的是避免嵌套的try..finally块可能会变得凌乱。例如。
A1 := TTest.Create;
try
A2 := TTest.Create;
try
{...}
finally
A2.Free;
end;
finally
A1.Free;
end;
Marco的代码降低了嵌套级别,但是需要对本地引用进行“预初始化”。
维多利亚提出了一个警告,即如果
A2
的析构函数在Marco的代码中失败,那么A1
将不会被释放。这将是一定的内存泄漏。但是,我认为任何析构函数一旦失败:因此,我能提供的最佳建议是:小心确保析构函数的正确性。