【问题标题】:In Delphi 5, can Free ever raise an exception?在 Delphi 5 中,Free 可以引发异常吗?
【发布时间】:2012-02-08 10:00:05
【问题描述】:

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

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

此代码假定Free 永远不会引发,因为例如,如果a.Free 引发,bc 的内存将被泄漏。这个假设合理吗?

【问题讨论】:

  • 为什么出于兴趣而投反对票?这是一个明确的问题,没有歧义,我真的很想知道答案。 耸耸肩
  • 我认为你在这里接受的答案太快了。
  • @NGLN:实际上同意(对不起 TOndrej),我认为大卫的回答要好得多。
  • @StuartGolodetz 没问题,我同意。

标签: delphi exception-handling delphi-5


【解决方案1】:

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

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

a := TMyObject.Create;
try
  b := TMyObject.Create;
  try
    ...
  finally
    b.Free;
  end;
finally
  a.Free;
end;

话虽如此,在析构函数中不要引发异常应该是一个设计原则。所以,在我看来,如果在析构函数中引发异常,那么你的程序就非常糟糕,这是完全合理的。此时泄漏的物体不必担心。如果您的析构函数引发了异常,那么您可能已经泄漏,因为该析构函数没有运行完成。

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

如果你只想要一个try/finally,那么记得这样写代码:

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 中的函数相同的名称,乍一看可能看起来很奇怪,但这样做是安全且良性的。当您拥有两个以上的对象时,这些助手自然会发挥作用。

【讨论】:

    【解决方案2】:

    取决于析构函数中发生了什么。

    【讨论】:

    • 完全正确,我删除了错误的分析器。没有考虑析构函数。这就是为什么析构函数不应该抛出异常。
    【解决方案3】:

    可能有两件事会导致SomeObj.Free 引发异常:

    1. SomeObj 类或其祖先实例的析构函数中出现未处理的异常。
    2. 由于未初始化变量SomeObj,类引用无效。

    在您的情况下,如果a.Free 因上述任何原因引发异常,则对象bc 将存在内存泄漏,并且由于析构函数中未处理的异常,对象a 内部可能存在一些泄漏.

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • Stuart 的假设是“Free 不能引发异常”,它显然可以。
      • 别忘了,a 对象在其析构函数引发异常时也会泄漏。
      • 我的意思是a 总是会泄漏,因为a 本身的内存肯定不会被释放,但你指出它的一些子对象可能是正确的。
      • 谢谢大家。我来自 C++ 背景,其中可以抛出的析构函数是一个严重的禁忌(因为之前的异常调用 std::terminate 导致堆栈被展开时抛出),所以我做了(什么结果是无效的)假设Delphi中的情况类似。
      • @StuartGolodetz 应用程序不会因 Delphi 双重异常而终止,但这并不意味着从析构函数引发异常是个好主意。你也不应该在 Delphi 中这样做。 :)
      【解决方案5】:

      当然,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;
      

      某种东西。这是一个关于你的代码实际上在做什么的问题,你是否也应该将 A.FREE 包装在 try..finally 部分中,尽管我猜你可能应该这样做。

      【讨论】:

      • 您的a.free; 不在任何try..finally 之外,如果b:=tobject.create; 引发异常,您尝试释放b(与c 相同)。跨度>
      • 此外,如果c.free 引发异常,您仍然有泄漏,因为您实际上还没有释放c
      • 无论如何,这是一种很好的编码风格值得商榷 - 但我确实说过你应该尝试..finally a.free;我只是在演示你如何做到这一点:-)
      • 在您的示例中,您绝对应该将 a.free; 也包含在 try..finally 中。如果您绝对 100% 确定代码永远不会引发异常,那么您就不需要它,但是您就不需要您确实包含的任何 try..finallys。跨度>
      猜你喜欢
      • 2014-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 2018-11-26
      • 1970-01-01
      • 2017-07-24
      相关资源
      最近更新 更多