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 回答
你的代码绝对正确无误 . 提高构造函数的异常是完全可敬的 . 如你所知,析构函数被调用 .
你问这个代码:
您担心在对象被销毁后将调用
Free
. 这不可能发生 . 如果在构造函数中引发异常,则它会向上传播调用堆栈 . 这发生在try
块开始之前,因此finally
块不会执行 . 实际上,a
的任务没有发生 .在
try
中移动创建将是灾难性的,实际上是一个非常常见的错误 . 假设你这样做了:现在如果引发异常,则调用
Free
,但是什么?变量a
未初始化 . 即使它不是,它仍然是双重自由 .好的,首先你可以在构造函数中引发异常,是的,它确实会调用析构函数 . 你展示的代码很好 . 但我认为你误解了你的代码所做的事情 . 并将构造函数置于try finally块中是错误的 . 我认为你缺少的一点是,如果你的构造函数失败,
try...finally
块永远不会被执行,所以free不会被执行 . 如果构造函数不成功,则不应该调用free,这就是为什么不应该将构造函数放在try...finally
块中 .首先,我要说你不能避免构造函数中的异常,因此它不能成为反模式 . 如果你检查Delphi源代码,你会发现在构造函数中引发异常的地方数量 . 例如
你唯一应该知道的是,如果异常从构造函数中逃脱,Delphi将自动调用析构函数 . 实际上,这意味着您的析构函数可能在部分构造的对象上执行,您有责任正确编写析构函数 . 请参阅TObject.Destroy文档,并特别注意以下引用:
PS一般来说你应该假设 each line of code 可能引发异常,但请不要成为偏执狂;)