首页 文章

Delphi在构造函数中引发异常

提问于
浏览
16

SITUATION

我打算写一个类,构造函数是我自己创建的一个,因为我需要初始化一些值 . 这是我到目前为止编写的代码:

type
 TCombinatorio = class(TObject)
  private
   valN, valK: integer;
   result: double;
  public
   property K: integer read valK;
   property N: integer read valN;
   constructor Create(valN: integer; valK: integer);
 end;

constructor TCombinatorio.Create(valN: Integer; valK: Integer);
begin
  inherited Create;
   Self.valN := valN;
   Self.valK := valK;

  if ((valN < 0) or (valK < 0)) then
   begin
    raise Exception.Create('N and K must be >= 0');
   end;

end;

由于我要做一些数学计算,我需要避免负数 .


QUESTION

我可以用这种方式在构造函数中调用异常吗?我正在以这种方式运行代码:

procedure TForm1.Button1Click(Sender: TObject);
var a: TCombinatorio; 
    b: string;   
begin

 a := TCombinatorio.Create(5,-2);

 try
  //some code
 finally
  a.Free; 
 end;

end;

正如你在这里看到的,我的构造函数有错误的参数,因为第二个是负数 . 我也无法理解(根据我的构造函数的代码)是否真的需要finally中的 a.Free ,因为当构造函数引发异常时,析构函数被调用 .

我想在try-finally块中包含 a := TCombinatorio.Create(5,-2); 以避免问题,但我不确定 . 你怎么看?

3 回答

  • 3

    你的代码绝对正确无误 . 提高构造函数的异常是完全可敬的 . 如你所知,析构函数被调用 .

    你问这个代码:

    a := TCombinatorio.Create(5,-2);
    try
      //some code
    finally
      a.Free; 
    end;
    

    您担心在对象被销毁后将调用 Free . 这不可能发生 . 如果在构造函数中引发异常,则它会向上传播调用堆栈 . 这发生在 try 块开始之前,因此 finally 块不会执行 . 实际上, a 的任务没有发生 .

    try 中移动创建将是灾难性的,实际上是一个非常常见的错误 . 假设你这样做了:

    // WARNING THIS CODE IS DEFECTIVE 
    try
      a := TCombinatorio.Create(5,-2);
      //some code
    finally
      a.Free; 
    end;
    

    现在如果引发异常,则调用 Free ,但是什么?变量 a 未初始化 . 即使它不是,它仍然是双重自由 .

  • 18

    好的,首先你可以在构造函数中引发异常,是的,它确实会调用析构函数 . 你展示的代码很好 . 但我认为你误解了你的代码所做的事情 . 并将构造函数置于try finally块中是错误的 . 我认为你缺少的一点是,如果你的构造函数失败, try...finally 块永远不会被执行,所以free不会被执行 . 如果构造函数不成功,则不应该调用free,这就是为什么不应该将构造函数放在 try...finally 块中 .

  • 2

    首先,我要说你不能避免构造函数中的异常,因此它不能成为反模式 . 如果你检查Delphi源代码,你会发现在构造函数中引发异常的地方数量 . 例如

    constructor TCustomForm.Create(AOwner: TComponent);
    begin
      // ... skipped some lines
            if not InitInheritedComponent(Self, TForm) then
              raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
    

    你唯一应该知道的是,如果异常从构造函数中逃脱,Delphi将自动调用析构函数 . 实际上,这意味着您的析构函数可能在部分构造的对象上执行,您有责任正确编写析构函数 . 请参阅TObject.Destroy文档,并特别注意以下引用:

    注意:如果异常从构造函数中转义,则会调用析构函数来销毁无法完全初始化的部分构造的对象实例 . 因此,析构函数应该在尝试释放它们之前检查已分配的资源(如句柄),因为它们的值可能为零 .

    PS一般来说你应该假设 each line of code 可能引发异常,但请不要成为偏执狂;)

相关问题