25

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.)

12 Answers 12

36

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.

  • 4
    Thank you for a concise and direct answer that doesn't digress into theory and 'language X is better than Y' territory. – Agi Hammerthief Jan 27 '15 at 11:46
56

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;.

  • 8
    You also have to catch the Exceptions in handleError() in the second case, no? – Juri Robl Jan 26 '15 at 15:34
  • 1
    You 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 from handleError as well) – njzk2 Jan 26 '15 at 18:41
  • 1
    I'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). – Alex Jan 27 '15 at 1:06
  • 1
    This answer doesn't really address the question of why C++ doesn't have finally, which is a lot more nuanced. – DeadMG Jan 27 '15 at 11:28
  • 1
    @AgiHammerthief The nested try is inside the catch 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 guarantee cleanUp runs is to catch everything, but the original code would allow exceptions originating in the catch (SpecificException e) block to propagate upwards. – Doval Jan 27 '15 at 12:47
22

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;
}
  • 2
    I think this is the best answer. Using a finally as a replacement for a generic exception just seems shitty. The correct use case is to cleanup resources or analogous operations. – Kik Jan 26 '15 at 15:03
  • 3
    Perhaps even more common is returning inside the try block, rather than inside the catch block. – Michael Anderson Jan 27 '15 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.) – Agi Hammerthief Jan 27 '15 at 11:35
15

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.

  • 1
    Perfect answer. Destructors are THE must-have feature in C++. – Thomas Eding Jan 28 '15 at 1:59
13

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".

9

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.

  • 4
    That's not sufficient reason for a finally, as you can prevent "unexpected" exceptions with catch(Object) or catch(...) catch-alls. – MSalters Jan 26 '15 at 15:01
  • 1
    Which sounds like a work around. Conceptually finally is cleaner. Though I must confess to rarely using it. – quickly_now Jan 27 '15 at 7:15
7

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. – Morwenn Jan 26 '15 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. – OldCurmudgeon Jan 26 '15 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 K Jan 26 '15 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 the finalise mechanism of other languages. – OldCurmudgeon Jan 26 '15 at 23:08
1

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.

  • 3
    In .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 not try finally; code using the latter is converted into code using only the former, by copying the content of the finally block in all spots of the code where it might need to execute. – supercat Jan 26 '15 at 18:16
  • @supercat nice, thanks for the extra info about Java. – Pieter B Jan 26 '15 at 21:31
1

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...)

1

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.

  • 1
    Your 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. – Agi Hammerthief Jan 27 '15 at 11:45
  • 1
    The 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. – James Anderson Jan 28 '15 at 2:02
0

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:

  1. Destroying/freeing/unlocking/closing/etc local resources (destructors are perfect for this).
  2. 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).

-9

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 Jan 28 '15 at 15:10

protected by gnat Jan 27 '15 at 13:20

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.