用于goto链的OOP等效于在出错时释放资源?

时间:2018-05-25 21:30:48

标签: oop delphi memory-management exception-handling

在C中我使用goto链在出错时释放资源,如建议here。使用Delphi我遇到了以下情况:我想优雅地处理内存耗尽并防止内存泄漏:

New(A);
A.DoSomething;
New(A.B);
A.B.DoSomething;
New(A.C);
A.C.DoSomething;

据我了解,检查内存耗尽的方法是捕获New抛出的异常。让我们说DoSomething函数在出错时抛出Exception。 SEI CERT的编码标准建议不要使用in-band error checkingusing exceptions for control flow,至少对于Java,我认为这是非常合理的。我不确定如何处理这种情况,牢记这些建议。我的想法是做一些像

这样的事情
function AllocStuff : TA;
begin
  New(Result);
  Result.B := nil;
  Result.C := nil;    
  Result.DoSomething;    
  New(Result.B);
  Result.B.DoSomething;
  New(Result.C);
  Result.C.DoSomething;
end;

捕获来电者的例外:

procedure QuestionableControlFlow;
var
  A : TA;
begin
  A := nil;
  try
    A := AllocStuff;
    DoSomethingWith(A);
    Dispose(A);
  except on E : Exception do
    begin
      if (A <> nil) then
        begin
          if (A.B <> nil) then
            begin
              if (A.C <> nil)  then
                begin
                  Dispose(A.C);
                end;
              Dispose(A.B);
            end;
          Dispose(A);                    
        end;
    end;
end;

这看起来像它一样糟糕吗?将gotoexcept混合似乎更糟糕,这是我到目前为止所能想到的。

2 个答案:

答案 0 :(得分:7)

在Delphi中,您使用try/finally来管理非托管资源。

例如

obj := TObject.Create;
try
  obj.DoSomething;
finally
  obj.Free;
end;

你绝对不会使用try/except,尽管这是一个常见的错误。这是为了处理与保证最终确定不同的异常。

当您需要在一个函数中处理多个非托管资源时,您可以嵌套try/finally块。当筑巢很深,可能会很乱。可以在此处找到处理该问题的一些想法:Avoiding nested try...finally blocks in Delphi

答案 1 :(得分:4)

您的AllocStuff()应该使用try/except来捕获错误,因此它不会返回无效数据:

function AllocStuff : TA;
begin
  New(Result);
  try
    Result.B := nil;
    Result.C := nil;    
    Result.DoSomething;    
    New(Result.B);
    try
      Result.B.DoSomething;
      New(Result.C);
      try
        Result.C.DoSomething;
      except
        Dispose(Result.C);
        raise;
      end;
    except
      Dispose(Result.B);
      raise;
    end;
  except
    Dispose(Result);
    raise;
  end;
end;

然后调用者可以使用try/finally释放AllocStuff()返回的任何内容:

procedure QuestionableControlFlow;
var
  A : TA;
begin
  A := AllocStuff;
  try
    DoSomethingWith(A);
  finally
    Dispose(A.C);
    Dispose(A.B);
    Dispose(A);
  end;
end;