Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
OOP等价于goto链,用于在出错时释放资源?_Oop_Delphi_Memory Management_Exception Handling - Fatal编程技术网

OOP等价于goto链,用于在出错时释放资源?

OOP等价于goto链,用于在出错时释放资源?,oop,delphi,memory-management,exception-handling,Oop,Delphi,Memory Management,Exception Handling,在C语言中,我使用goto链在出错时释放资源,这是推荐的。在使用Delphi的过程中,我遇到了以下情况,我希望能够优雅地处理内存耗尽问题,并防止内存泄漏: New(A); A.DoSomething; New(A.B); A.B.DoSomething; New(A.C); A.C.DoSomething; 据我所知,检查内存耗尽的方法是捕获New引发的异常。假设DoSomething函数在出错时都抛出Exception。SEI CERT的编码标准建议反对和反对,至少对Java是这样,我觉得这

在C语言中,我使用goto链在出错时释放资源,这是推荐的。在使用Delphi的过程中,我遇到了以下情况,我希望能够优雅地处理内存耗尽问题,并防止内存泄漏:

New(A);
A.DoSomething;
New(A.B);
A.B.DoSomething;
New(A.C);
A.C.DoSomething;
据我所知,检查内存耗尽的方法是捕获
New
引发的异常。假设
DoSomething
函数在出错时都抛出
Exception
。SEI CERT的编码标准建议反对和反对,至少对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;
程序问题控制流程;
变量
A:TA;
开始
A:=零;
尝试
A:=AllocStuff;
(A)剂量;
处置(A);
E上除外:例外
开始
如果(零)那么
开始
如果(A.B.nil)那么
开始
如果(交流零)那么
开始
处置(A.C.);
结束;
处置(A.B);
结束;
处置(A);
结束;
结束;
结束;

这和看起来一样糟糕吗?将
goto
混合,除了
似乎更糟,这是迄今为止我所能想到的。

在Delphi中,您使用
try/finally
作为非托管资源生存期

比如说

obj := TObject.Create;
try
  obj.DoSomething;
finally
  obj.Free;
end;
尽管这是一个常见的错误,但绝对不要使用
try/except
。这是为了处理不同于保证最终确定的异常

当您需要在一个函数中处理多个非托管资源时,可以嵌套
try/finally
块。当筑巢很深时,可能会很混乱。这里可以找到一些处理方法:

您的
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;

goto
通常被认为是邪恶的,应该很少使用,如果有的话。对于C++,更好的解决方案是使用RAII,然后在堆栈上创建对象,并让析构函数在对象超出范围时释放资源。不过,C和Delphi没有RAII,也没有带析构函数的基于堆栈的对象。在Delphi中,您可以使用接口模拟RAII。嵌套成员应在其销毁方法中按主类型进行处理。Manual Dispose()的错误非常严重(即使它工作正常,编码正确),并且破坏了封装的最简单好处。正如David在回答中所写的,只需正确定义对象及其嵌套成员,然后通过try/finally使用它来保护资源。@Arnaud,封装是正确的,但对于问题示例,此回答正确地考虑了分配器(构造函数)中的异常,并正确地释放了嵌套的已分配资源。在这种情况下,您不能使用
try..finally
,因为当分配器返回时,嵌套成员必须保持分配状态。@Victoria Asker认为goto对此是有效的,所以我不会对Q中显示的技术挂断电话。如果我能在这个答案中加上+10,我会!:)