The Free
method itself does not explicitly throw an exception, but it calls a Destroy
virtual destructor, which can certainly throw an exception.
So, if you want to be sure that all your objects are destroyed, even if one of the destructors throws an exception, you will get the following code:
a := TMyObject.Create; try b := TMyObject.Create; try ... finally b.Free; end; finally a.Free; end;
Having said that, there should be a design principle that you make no exceptions in the destructor. So, in my opinion, it is completely reasonable to assume that if an exception occurs in the destructor, then your program is largely closed. Leakage of objects at this moment is nothing to worry about. If your destructor throws an exception, you are probably already leaking because this destructor has not completed.
So, in my opinion, it is quite reasonable to combine some calls on Free
and, of course, you avoid deeply nested try
/ finally
, which is worth striving for.
If you want only one try
/ finally
, then remember to write the code as follows:
a := nil; b := nil; try a := TMyObject.Create; b := TMyObject.Create; ... finally b.Free; a.Free; end;
In my own code base, I have helper methods that make this cleaner. Then the code might look like this:
InitialiseNil(a, b); try a := TMyObject.Create; b := TMyObject.Create; ... finally FreeAndNil(b, a); end;
I gave my FreeAndNil
the same name as the function in SysUtils
, which at first glance may seem strange, but it is safe and friendly. Naturally, these helpers come to their senses when you have more than two objects.