Some languages (such as C++ and early versions of PHP) don't support the finally
part of a try ... catch ... finally
construct. Is finally
ever necessary? Because the code in it always runs, why wouldn't/shouldn't I just place that code after a try ... catch
block without a finally
clause? Why use one? (I'm looking for a reason/motivation for using/not using finally
, not a reason to do away with 'catch' or why it's legal to do so.)
-
Comments are not for extended discussion; this conversation has been moved to chat.– maple_shaft ♦Commented Jan 28, 2015 at 15:05
12 Answers
In addition to what others have said, it's also possible for an exception to be thrown inside the catch clause. Consider this:
try {
throw new SomeException();
} catch {
DoSomethingWhichUnexpectedlyThrows();
}
Cleanup();
In this example, the Cleanup()
function never runs, because an exception gets thrown in the catch clause and the next highest up catch in the call stack will catch that. Using a finally block removes this risk, and makes the code cleaner to boot.
-
4Thank you for a concise and direct answer that doesn't digress into theory and 'language X is better than Y' territory. Commented Jan 27, 2015 at 11:46
As others have mentioned, there's no guarantee that code after a try
statement will execute unless you catch every possible exception. That said, this:
try {
mightThrowSpecificException();
} catch (SpecificException e) {
handleError();
} finally {
cleanUp();
}
can be rewritten1 as:
try {
mightThrowSpecificException();
} catch (SpecificException e) {
try {
handleError();
} catch (Throwable e2) {
cleanUp();
throw e2;
}
} catch (Throwable e) {
cleanUp();
throw e;
}
cleanUp();
But the latter requires you to catch all unhandled exceptions, duplicate the cleanup code, and remember to re-throw. So finally
isn't necessary, but it's useful.
C++ doesn't have finally
because Bjarne Stroustrup believes RAII is better, or at least suffices for most cases:
Why doesn't C++ provide a "finally" construct?
Because C++ supports an alternative that is almost always better: The "resource acquisition is initialization" technique (TC++PL3 section 14.4). The basic idea is to represent a resource by a local object, so that the local object's destructor will release the resource. That way, the programmer cannot forget to release the resource.
1 The specific code to catch all exceptions and rethrow without losing stack trace information varies by language. I have used Java, where the stack trace is captured when the exception is created. In C# you'd just use throw;
.
-
8You also have to catch the Exceptions in
handleError()
in the second case, no? Commented Jan 26, 2015 at 15:34 -
1You may also be throwing an error. I would rephrase that
catch (Throwable t) {}
, with the try .. catch block around the entire initial block (to catch throwables fromhandleError
as well)– njzk2Commented Jan 26, 2015 at 18:41 -
1I'd actually add the extra try-catch you omitted when calling
handleErro();
which will make it even better argument as to why finally blocks are useful (even though that was not the original question).– AlexCommented Jan 27, 2015 at 1:06 -
1This answer doesn't really address the question of why C++ doesn't have
finally
, which is a lot more nuanced.– DeadMGCommented Jan 27, 2015 at 11:28 -
2@AgiHammerthief The nested
try
is inside thecatch
for the specific exception. Secondly, it's possible you don't know whether you can handle the error successfully until you've examined the exception, or that the cause of the exception also prevents you from handling the error (at least at that level). That's fairly common when doing I/O. The rethrow is there because the only way to guaranteecleanUp
runs is to catch everything, but the original code would allow exceptions originating in thecatch (SpecificException e)
block to propagate upwards.– DovalCommented Jan 27, 2015 at 12:47
finally
blocks are usually used to clear up resources which can help with readability when using multiple return statements:
int DoSomething() {
try {
open_connection();
return get_result();
}
catch {
return 2;
}
finally {
close_connection();
}
}
vs
int DoSomething() {
int result;
try {
open_connection();
result = get_result();
}
catch {
result = 2;
}
close_connection();
return result;
}
-
3Perhaps even more common is returning inside the try block, rather than inside the catch block. Commented Jan 27, 2015 at 2:56
-
To my mind, the code doesn't adequately explain the use of
finally
. (I'd use code as in the second block as multiple return statements are discouraged where I work.) Commented Jan 27, 2015 at 11:35
As you've apparently already surmised, yes, C++ provides the same capabilities without that mechanism. As such, strictly speaking, the try
/finally
mechanism is not really necessary.
That said, doing without it does impose some requirements on the way the rest of the language is designed. In C++ the same set of actions is embodied in a class' destructor. This works primarily (exclusively?) because destructor invocation in C++ is deterministic. This, in turn, leads to some rather complex rules about object lifetimes, some of which are decidedly non-intuitive.
Most of the other languages provide some form of garbage collection instead. While there are things about garbage collection that are controversial (e.g., its efficiency relative to other methods of memory management) one thing generally isn't: the exact time when an object will be "cleaned up" by the garbage collector is not tied directly to the scope of the object. This prevents its use when cleanup needs to be deterministic either when it's simply required for correct operation, or when dealing with resources so precious that their cleanup most not be delayed arbitrarily. try
/finally
provides a way for such languages to deal with those situations that require that deterministic cleanup.
I think those claiming that C++ syntax for this capability is "less friendly" than Java's are rather missing the point. Worse, they're missing a much more crucial point about the division of responsibility that goes far beyond syntax, and has a great deal more to do with how code is designed.
In C++, this deterministic cleanup happens in the object's destructor. That means the object can be (and normally should be) designed to clean up after itself. This goes to the essence of object oriented design--a class should be designed to provide an abstraction, and enforce its own invariants. In C++, one does precisely that--and one of the invariants for which it provides is that when the object is destroyed, the resources controlled by that object (all of them, not just memory) will be destroyed correctly.
Java (and similar) are somewhat different. While they do (sort of) support a finalize
that could theoretically provide similar capabilities, the support is so weak that it's basically unusable (and in fact, essentially never used).
As a result, rather than the class itself being able to do the required cleanup, the client of the class needs to take steps to do so. If we do a sufficiently short-sighted comparison, it can at first glance appear that this difference is fairly minor and Java is quite competitive with C++ in this respect. We end up with something like this. In C++, the class looks something like this:
class Foo {
// ...
public:
void do_whatever() { if (xyz) throw something; }
~Foo() { /* handle cleanup */ }
};
...and the client code looks something like this:
void f() {
Foo f;
f.do_whatever();
// possibly more code that might throw here
}
In Java we exchange a little more code where the object is used for a little less in the class. This initially looks like a pretty even trade-off. In reality, it's far from it though, because in most typical code we only define the class in one place, but we use it many places. The C++ approach means we only write that code to handle the cleanup in one place. The Java approach means we have to write that code to handle the cleanup many times over, in many places--every place we use an object of that class.
In short, the Java approach basically guarantees that many abstractions we try to provide are "leaky"--any and every class that requires deterministic cleanup obliges the client of the class to know about the details of what to cleanup and how to do the cleanup, rather than those details being hidden in the class itself.
Although I've called it "the Java approach" above, try
/finally
and similar mechanisms under other names aren't entirely restricted to Java. For one prominent example, most (all?) of the .NET languages (e.g., C#) provide the same.
Recent iterations of both Java and C# also provide something of a halfway point between "classic" Java and C++ in this regard. In C#, an object that wants to automate its cleanup can implement the IDisposable
interface, which provides a Dispose
method that's (at least vaguely) similar to a C++ destructor. While this can be used via a try
/finally
like in Java, C# automates the task a little more with a using
statement that lets you define resources that will be created as a scope is entered, and destroyed when the scope is exited. Though still well short of the level of automation and certainty provided by C++, this is still a substantial improvement over Java. In particular, the class designer can centralize the details of how to dispose of the class in its implementation of IDisposable
. All that's left for the client programmer is the lesser burden of writing a using
statement to assure that the IDisposable
interface will be used when it should be. In Java 7 and newer, the names have been changed to protect the guilty, but the basic idea is basically identical.
-
1Perfect answer. Destructors are THE must-have feature in C++. Commented Jan 28, 2015 at 1:59
-
Can't believe nobody else has raised this (no pun intended) - you don't need a catch clause!
This is perfectly reasonable:
try
{
AcquireManyResources();
DoSomethingThatMightFail();
}
finally
{
CleanUpThoseResources();
}
No catch clause anywhere is sight, because this method can't do anything useful with those exceptions; they are left to propagate back up the call stack to a handler that can. Catching and re-throwing exceptions in every method is a Bad Idea, especially if you're just re-throwing the same exception. It completely goes against how Structured Exception Handling is supposed to work (and is pretty close to returning an "error code" from every method, just in the "shape" of an Exception).
What this method does have to do, though, it to clean up after itself, so that the "Outside World" never needs to know anything about the mess that it got itself into. The finally clause does just that - no matter how the called methods behave, the finally clause will be executed "on the way out" of the method (and the same is true for every finally clause between the point at which the Exception is thrown and the eventual catch clause that handles it); each one is run as the call stack "unwinds".
What would happen if and exception was thrown that you were not expecting. The try would exit in the middle of it and no catch clause is executed.
The finally block is to help with that and ensure that no matter the exception the cleanup will happen.
-
4That's not sufficient reason for a
finally
, as you can prevent "unexpected" exceptions withcatch(Object)
orcatch(...)
catch-alls.– MSaltersCommented Jan 26, 2015 at 15:01 -
1Which sounds like a work around. Conceptually finally is cleaner. Though I must confess to rarely using it. Commented Jan 27, 2015 at 7:15
Some languages offer both constructors and destructors for their objects (e.g. C++ I believe). With these languages you can do most (arguably all) of what is usually done in finally
in a destructor. As such - in those languages - a finally
clause may be superfluous.
In a language without destructors (e.g. Java) it is difficult (maybe even impossible) to achieve correct cleanup without the finally
clause. NB - In Java there is a finalise
method but there is no guarantee it will ever be called.
-
It may be useful to note that destructors help with cleaning resources when destruction is deterministic. If we don't know when the object will be destructed and/or garbage-collected, then destructors are not safe enough.– MorwennCommented Jan 26, 2015 at 20:58
-
@Morwenn - Good point. I hinted at it with my reference to Java's
finalise
but I would prefer not to get into the political arguments around destructors/finalies at this time. Commented Jan 26, 2015 at 21:41 -
In C++ destruction is deterministic. When the scope containing an automatic object exits (e.g. it gets popped off the stack), its destructor is called. (C++ allows you to allocate objects on the stack, not just the heap.)– Rob KCommented Jan 26, 2015 at 22:10
-
@RobK - And this is the exact functionality of a
finalise
but with both an extensible flavor and an oop-like mechanism - very expressive and comparable to thefinalise
mechanism of other languages. Commented Jan 26, 2015 at 23:08
Try finally and try catch are two different things which only share the: "try" keyword. Personally I would have like to see that different. The reason you see them together is because exceptions produce a "jump".
And try finally is designed to run code even if programming flow jumps out. Whether that be because of an exception or any other reason. It's a neat way to acquire a resource and make sure it's cleaned up after without having to worry about jumps.
-
3In .NET they are implemented using separate mechanisms; in Java, however, the only construct recognized by the JVM is semantically equivalent to "on error goto", a pattern which directly supports
try catch
but nottry finally
; code using the latter is converted into code using only the former, by copying the content of thefinally
block in all spots of the code where it might need to execute.– supercatCommented Jan 26, 2015 at 18:16 -
Since this question doesn't specify C++ as a language I'll consider a mix of C++ and Java, since they take a different approach to object destruction, which is getting suggested as one of the alternatives.
Reasons you might use a finally block, rather than code after the try-catch block
you return early from the try block: Consider this
Database db = null; try { db = open_database(); if(db.isSomething()) { return 7; } return db.someThingElse(); } finally { if(db!=null) db.close(); }
compared with:
Database db = null; int returnValue = 0; try { db = open_database(); if(db.isSomething()) { returnValue = 7; } else { returnValue = db.someThingElse(); } } catch(Exception e) { if(db!=null) db.close(); } return returnValue;
you return early from the catch block(s) : Compare
Database db = null; try { db = open_database(); db.doSomething(); } catch (DBIntegrityException e ) { return 7; } catch (DBIsADonkeyException e ) { return 11; } finally { if(db!=null) db.close(); }
vs:
Database db = null; try { db = open_database(); db.doSomething(); } catch (DBIntegrityException e ) { if(db!=null) db.close(); return 7; } catch (DBIsADonkeyException e ) { if(db!=null) db.close(); return 11; } db.close();
You rethrow exceptions. Compare:
Database db = null; try { db = open_database(); db.doSomething(); } catch (DBIntegrityException e ) { throw convertToRuntimeException(e,"DB was wonkey"); } finally { if(db!=null) db.close(); }
vs:
Database db = null; try { db = open_database(); db.doSomething(); } catch (DBIntegrityException e ) { if(db!=null) db.close(); throw convertToRuntimeException(e,"DB was wonkey"); } if(db!=null) db.close();
These examples don't make it seem too bad, but often you have several of these cases interacting and more than one exception/resource type in play. finally
can help keep your code from becoming a tangled maintenance nightmare.
Now in C++ these can be handled with scope based objects. But IMO there are two disadvantges to this approach 1. syntax is less friendly. 2. Order of construction being the reverse of order of destruction can make things less clear.
In Java you can't hook the finalize method to do your cleanup since you don't know when it will happen - (well you can but thats a path filled with fun race conditions - JVM has a lot of scope in deciding when it destroys things - often its not when you expect it - either earlier or later than you might expect - and that can change as the hot-spot compiler kicks in... sigh...)
All that is logicaly "necessary" in a programming language are the instructions:
assignment a = b
subtract a from b
goto label
test a = 0
if true goto label
Any algorithm can be implemented using only the instructions above, all other language constructs are there to make programs easier to write and more understandable to other programmers.
See oldie worldy computer for actual hardware using such a minimal instruction set.
-
1Your answer is certainly true, but I don't code in assembly; it's too painful. I'm asking why use a feature I don't see the point of in languages that support it, not what the bare minimum instruction set of a language is. Commented Jan 27, 2015 at 11:45
-
1The point is any language implementing just these 5 operations can implement any algorithm -- albeit rather tortuously. Most vers/operators in high level languages are not "necessary" if the goal is merely to implement an algorithm. If the goal is to have rapid development of readable maintainable code then most are necessary but "readable" and "maintainable" are not measurable and extremely subjective. The nice language developers put in lots of features: if you don't have a use for some of them then don't use them. Commented Jan 28, 2015 at 2:02
Actually the bigger gap to me is usually in the languages that support finally
but lack destructors, because you can model all the logic associated with "cleanup" (which I'll separate into two categories) through destructors at a central level without manually dealing with cleanup logic in every relevant function. When I see C# or Java code doing things like manually unlocking mutexes and closing files in finally
blocks, that feels outdated and kinda like C code when all of that is automated in C++ through destructors in ways that frees humans of that responsibility.
However, I would still find a mild convenience if C++ included finally
and it's because there's two types of clean-ups:
- Destroying/freeing/unlocking/closing/etc local resources (destructors are perfect for this).
- Undoing/rolling back external side effects (destructors are adequate for this).
The second one at least doesn't map so intuitively to the idea of resource destruction, though you can do it just fine with scope guards which automatically roll back changes when they're destroyed prior to being committed. There finally
arguably provides at least a slightly (just by a teeny bit) more straightforward mechanism for the job than scope guards.
However, an even more straightforward mechanism would be a rollback
block which I've never seen in any language before. It's kind of a pipe dream of mine if I ever designed a language that involved exception-handling. It would resemble this:
try
{
// Cause external side effects. These side effects should
// be undone if we don't finish successfully.
}
rollback
{
// Reverse external side effects. This block is *only* executed
// if the 'try' block above faced a premature return out
// of the function. It is different from 'finally' which
// gets executed regardless of whether or not the function
// exited prematurely. This block *only* gets executed if we
// exited prematurely from the try block so that we can undo
// whatever side effects it failed to finish making. If the try
// block succeeded and didn't face a premature exit, then we
// don't want this block to execute.
}
That would be the most straightforward way to model side effect rollbacks, while destructors are pretty much the perfect mechanism for local resource cleanups. Now it only saves a couple extra lines of code from the scope guard solution, but the reason I so want to see a language with this in there is that side effect rollback tends to be the most neglected (but trickiest) aspect of exception-handling in languages that revolve around mutability. I think this feature would encourage developers to think about exception-handling the proper way in terms of rolling back transactions whenever functions cause side effects and fail to complete and, as a side bonus when people see how difficult it can be to do rollbacks properly, they might favor writing more functions free of side effects in the first place.
There are also some obscure cases where you just want to do miscellaneous things no matter what upon exiting a function, regardless of how it exited, like maybe logging a time stamp. There finally
is arguably the most straightforward and perfect solution for the job, since trying to instantiate an object only to use its destructor for the sole purpose of logging a timestamp just feels really weird (even though you can do it just fine and quite conveniently with lambdas).
Like so many other unusual things about the C++ language, the lack of a try/finally
construct is a design flaw, if you can even call it that in a language that frequently appears to not have had any actual design work done at all.
RAII (the use of scope-based deterministic destructor invocation on stack-based objects for cleanup) has two serious flaws. The first is that it requires the use of stack based objects, which are an abomination that violate the Liskov Substitution Principle. There are plenty of good reasons why no other OO language before or since C++ has used them--within epsilon; D doesn't count as it's based heavily on C++ and has no market share anyway--and explaining the problems they cause is beyond the scope of this answer.
Second, what finally
can do is a superset of object destruction. Much of what is done with RAII in C++ would be described in the Delphi language, which does not have garbage collection, with the following pattern:
myObject := MyClass.Create(arguments);
try
doSomething(myObject);
finally
myObject.Free();
end;
This is the RAII pattern made explicit; if you were to create a C++ routine that contains only the equivalent to the first and third lines above, what the compiler would generate would end up looking like what I wrote in its basic structure. And because it's the only access to the try/finally
construct that C++ provides, C++ developers end up with a rather myopic view of try/finally
: when all you have is a hammer, everything starts to look like a destructor, so to speak.
But there are other things that an experienced developer can do with a finally
construct. It's not about deterministic destruction, even in the face of an exception being raised; it's about deterministic code execution, even in the face of an exception being raised.
Here's another thing you might commonly see in Delphi code: A dataset object with user controls bound to it. The dataset holds data from an external source, and the controls reflect the state of the data. If you're about to load a bunch of data into your dataset, you will want to temporarily disable the data binding so that it doesn't do strange things to your UI, trying to update it over and over with every new record that's entered, so you would code it like this:
dataset.DisableControls();
try
LoadData(dataset);
finally
dataset.EnableControls();
end;
Clearly, there is no object being destroyed here, and no need for one. The code is simple, concise, explicit, and efficient.
How would this be done in C++? Well, first you would have to code up an entire class. It would probably be called DatasetEnabler
or somesuch thing. Its entire existence would be as a RAII helper. Then you would need to do something like this:
dataset.DisableControls();
{
raiiGuard = DatasetEnabler(dataset);
LoadData(dataset);
}
Yes, those apparently-superfluous curly braces are necessary to manage the proper scoping and ensure that the dataset gets re-enabled immediately and not at the end of the method. So what you end up with does not take less lines of code (unless you use Egyptian braces). It requires a superfluous object to be created, which has overhead. (Isn't C++ code is supposed to be fast?) It is not explicit, but instead relies upon compiler magic. The code that gets executed is described nowhere in this method, but instead resides in an entirely different class, possibly in an entirely different file. In short, it is in no way a better solution than being able to write the try/finally
block yourself.
This kind of problem is common enough in language design that there's a name for it: abstraction inversion. It occurs when a high-level construct is built on top of a low-level construct, and then the low-level construct is not directly supported in the language, requiring those who wish to use it to re-implement it in terms of the high-level construct, often at steep penalties to both code readability and efficiency.
-
Comments are meant to clarify or improve a question and answer. If you would like to have a discussion about this answer then please go to the chat room. Thank you.– maple_shaft ♦Commented Jan 28, 2015 at 15:10