在Delphi5中,Free是否会引发异常?

在Delphi5中,Free是否会引发异常?,delphi,exception-handling,delphi-5,Delphi,Exception Handling,Delphi 5,在Delphi5中,我目前编写的代码在finally块中对多个变量调用Free,例如 ... finally a.Free; b.Free; c.Free; end; 此代码假定Free永远无法提升,因为例如,如果a.Free提升,则b和c的内存将泄漏。这个假设是否合理?取决于析构函数中发生的情况。当然,FREE可以发出异常-因此,是的,如果A.FREE发出异常,将泄漏代码中的内存,B.FREE和C.FREE将不会被调用 问题是,您希望处理异常还是让它们发生?这将取决于

在Delphi5中,我目前编写的代码在
finally
块中对多个变量调用
Free
,例如

...
finally
    a.Free;
    b.Free;
    c.Free;
end;

此代码假定
Free
永远无法提升,因为例如,如果
a.Free
提升,则
b
c
的内存将泄漏。这个假设是否合理?

取决于析构函数中发生的情况。

当然,FREE可以发出异常-因此,是的,如果A.FREE发出异常,将泄漏代码中的内存,B.FREE和C.FREE将不会被调用

问题是,您希望处理异常还是让它们发生?这将取决于您的代码的用途,以及其他开发人员是否会使用它(例如)。为了防止任何内存泄漏,您应该嵌套try..finally部分

a:=tobject.create;
try
  b:=tobject.create;
  try
    c:=tobject.create;

    ...

  finally
    c.free;
  end;
finally
  b.free;
end;
a.free;

有点像。这是一个关于你的代码实际上在做什么的问题,比如你是否也应该在try中包装a.FREE.最后一节,尽管我猜你可能应该这样做。

如果你的a.FREE引发异常,a(取决于析构函数从a的对象字段中释放了多少),b和c对象将被泄漏,因为执行将被中断。无论如何,如果析构函数引发错误,那么它就有问题了。因此,您应该使用try..finally块来保护代码,但是您应该验证析构函数在任何情况下都不会给您带来错误。

Free方法本身不会显式引发异常,但它调用虚拟析构函数
Destroy
,这肯定会引发异常

因此,如果要确保所有对象都已销毁,即使其中一个析构函数引发异常,最终也会生成如下代码:

a := TMyObject.Create;
try
  b := TMyObject.Create;
  try
    ...
  finally
    b.Free;
  end;
finally
  a.Free;
end;
a := nil;
b := nil;
try
  a := TMyObject.Create;
  b := TMyObject.Create;
  ...
finally
  b.Free;
  a.Free;
end;
InitialiseNil(a, b);
try
  a := TMyObject.Create;
  b := TMyObject.Create;
  ...
finally
  FreeAndNil(b, a);
end;
话虽如此,不在析构函数中引发异常应该是一个设计原则。所以,在我看来,如果析构函数中出现了异常,那么程序就几乎被套住了,这样的观点是完全合理的。在这一点上泄漏的物体不需要担心。如果您的析构函数引发了异常,那么您可能已经在泄漏,因为该析构函数没有运行到完成

因此,在我看来,将一些对
Free
的调用组合在一起是完全合理的,当然,您可以避免深度嵌套
尝试最后的
/
,这是值得努力的

如果您只需要一个
尝试
/
最后
,请记住这样编写代码:

a := TMyObject.Create;
try
  b := TMyObject.Create;
  try
    ...
  finally
    b.Free;
  end;
finally
  a.Free;
end;
a := nil;
b := nil;
try
  a := TMyObject.Create;
  b := TMyObject.Create;
  ...
finally
  b.Free;
  a.Free;
end;
InitialiseNil(a, b);
try
  a := TMyObject.Create;
  b := TMyObject.Create;
  ...
finally
  FreeAndNil(b, a);
end;
在我自己的代码库中,我有一些帮助器方法,可以使它更干净。然后代码可以如下所示:

a := TMyObject.Create;
try
  b := TMyObject.Create;
  try
    ...
  finally
    b.Free;
  end;
finally
  a.Free;
end;
a := nil;
b := nil;
try
  a := TMyObject.Create;
  b := TMyObject.Create;
  ...
finally
  b.Free;
  a.Free;
end;
InitialiseNil(a, b);
try
  a := TMyObject.Create;
  b := TMyObject.Create;
  ...
finally
  FreeAndNil(b, a);
end;

我给我的
FreeAndNil
起了与
SysUtils
中的函数相同的名字,乍一看可能有些奇怪,但这样做是安全和有益的。当然,当您有两个以上的对象时,这些帮助程序也会自动出现。

可能有两种情况会导致
SomeObj.Free
引发异常:

  • 类的
    SomeObj
    实例的析构函数中或其祖先的析构函数中存在未处理的异常
  • 由于未初始化变量
    SomeObj
    ,类引用无效

  • 在您的情况下,如果
    a.Free
    因上述任何原因引发异常,则对象
    b
    c
    将出现内存泄漏,并且可能由于析构函数中未处理的异常而导致对象
    a
    内部发生泄漏。

    完全正确,我删除了错误的anser。没有考虑析构函数。这就是为什么析构函数不应该抛出异常
    在任何
    try
    之外。
    最后
    ,如果
    b:=tobject.create,则尝试释放
    b
    引发异常(与
    c
    相同)。此外,如果
    c.free
    引发异常,您仍然存在漏洞,因为您实际上没有释放
    c
    。这是一种值得商榷的编码风格,但我确实说过,您可能应该尝试..最后是a.free;我只是在演示如何做到这一点:-)在您的示例中,您肯定应该将
    a.free包装起来中尝试
    最后也尝试
    。如果你绝对100%确定代码永远不会引发异常,那么你就不需要它,但你也不需要任何
    尝试
    最后
    你确实包含了。斯图尔特的假设是“免费不能引发异常”,而且它显然可以。别忘了,如果
    a
    对象的析构函数引发异常,该对象也会泄漏。我实际上是说
    a
    将始终泄漏,因为
    a
    本身的内存肯定不会被释放,但您可以指出它的一些子对象可能会泄漏。谢谢大家。我从C++背景中来这里,其中抛出的析构函数是一个严重的NO(因为抛出堆栈时,由于先前的异常调用,堆栈正在被解开:<代码> STD::终止< /COD>),所以我做了(什么是无效的)假设Delphi中的情况与此类似。@StuartGolodetz应用程序不会在Delphi双重异常时终止,但这并不意味着从析构函数引发异常是个好主意。您也不应该在Delphi中执行此操作。:)为什么否决票是出于兴趣?这是一个明确的问题,没有歧义,我真的想知道答案。Shrugs我觉得你在这里接受答案太快了。@NGLN:事实上同意(对不起,TOndrej),我认为大卫的答案要好得多。@StuartGolodetz没问题,我同意。