67

Exception handling in C++ is limited to try/throw/catch. Unlike Object Pascal, Java, C# and Python, even in C++ 11, the finally construct has not been implemented.

I have seen an awful lot of C++ literature discussing "exception safe code". Lippman writes that exception safe code is an important but advanced, difficult topic, beyond the scope of his Primer - which seems to imply that safe code is not fundamental to C++. Herb Sutter devotes 10 chapters to the topic in his Exceptional C++ !

Yet it seems to me that many of the problems encountered when attempting to write "exception safe code" could be quite well solved if the finally construct was implemented, allowing the programmer to ensure that even in the event of an exception, the program can be restored to a safe, stable, leak-free state, close to the point of allocation of resources and potentially problematic code. As a very experienced Delphi and C# programmer I use try.. finally blocks quite extensively in my code, as do most programmers in these languages.

Considering all the 'bells and whistles' implemented in C++ 11, I was astonished to find that 'finally' was still not there.

So, why has the finally construct never been implemented in C++? It's really not a very difficult or advanced concept to grasp and goes a long ways towards helping the programmer to write 'exception safe code'.

Vector
  • 3,180
  • 3
  • 22
  • 25
  • 27
    Why no finally? Because you release things in the destructor which fires automatically when the object (or smart pointer) leaves scope. Destructors are superior to finally{} since it separates workflow from cleanup logic. Just as you wouldn't want calls to free() cluttering up your workflow in a garbage collected language. – mike30 May 09 '13 at 20:11
  • 2
  • 8
    Asking the question, "Why is there no finally in C++, and what techniques for exception handling are used in its place?" is valid and on topic for this site. The existing answers cover this well, I think. Turning it into a discussion on "Are the C++ designers' reasons for not including finally worthwhile?" and "Should finally be added to C++?" and carrying on the discussion across comments on the question and every answer doesn't fit the model of this Q&A site. – Josh Kelley May 09 '13 at 21:31
  • 1
    Another alternative to "Why finally when you have RAII?" could be - Even if the class/type you are (forced into) using does not have a destructor (e.g., FILE *), and even in scenario of calling other code that can throw exceptions, one can, by following a strict SESE regimen, ensure proper clean-up of acquired resources. – Happy Green Kid Naps May 09 '13 at 21:46
  • @Kaz. Separation of concerns is not syntax. Line delimiters are not a valid analogy as they are syntax. Mixing clean up in the workflow makes the program less readable. Look at the average handle heavy Java program and you'll see what I mean. – mike30 May 10 '13 at 13:10
  • This is one of my favorite interview questions. It goes along with comparing and contrasting Java and C++.

    No finally needed in C++.

    – Bill Door May 10 '13 at 15:26
  • 1
    Yes, it is just syntax, when I have one single cleanup to do that is not repeated anywhere else in the program, and I'm required to write a bunch of syntax for a class, and then move the syntax for the cleanup out of the function and into that class. Keeping stuff together is important. That's why lambdas are important. Lambdas let you use a lexical scope as a first class object. And the thing about lexical scopes is that in a program there can be many of them, each quite unique. Classes are useful for cookie-cutter instantiation of many identical objects. – Kaz May 12 '13 at 07:38
  • 2
    If you have finally, you already have separation of concerns: the main code block is here, and the cleanup concern is taken care of here. – Kaz May 12 '13 at 07:40
  • 2
    @Kaz. The difference is implicit vs explicit clean up. A destructor gives you automatic clean up similar to how a plain old primitive is cleaned up as it pops off the stack. You don't need to make any explicit clean up calls and can focus on your core logic. Imagine how convoluted it would be if you had to clean up stack allocated primitives in a try/finally. Implicit clean up is superior. The comparison of class syntax to anonymous functions is not relevant. Although by passing first class functions to a function which releases a handle could centralize manual cleanup. – mike30 May 15 '13 at 13:19
  • 1
    @mike30 - the problem with this is that I see endless lines of C++ code making use of pointers and new(). Not to do so forces some very difficult constaints upon the programmer - which is why it so prevelant. I think this means that a finally construct would be most useful, in spite of the possible 'superiority' of implicit clean-up, which I don't necessarily deny. I am chiefly a Delphi programmer - I do not enjoy reading and writing all those try..finally blocks, and so over the years I have developed ways to minimize them. – Vector May 15 '13 at 16:14
  • @mike30 A destructor is not implicit; you have to write it and then explicitly instantiate the object! The behavior of calling it is then implicit. So is the invocation of a finally block. Man, people like confuse things ... – Kaz May 15 '13 at 16:26
  • 1
    @Kaz. Yes the library creator has to explicitly write the destructor. But the library consumer gets it for free. A finally block is the reverse in that the library writer doesn't handle the clean up, but the consumer must. Consumer code greatly outwighs the library code in volume. Getting it for free at the point of usage greatly outweighs. How often do you manually close a connection in a non-deterministic language? What if you forget? – mike30 May 15 '13 at 16:55
  • @Mikey. When using RAII you don't use raw pointers or the new keyword in C++. You use smart pointer templates. With minimal knowledge you can approach the freedom of garbage collection for all resources, not just memory. (from the perspective of the library consumer, not creator) – mike30 May 15 '13 at 16:59
  • 2
    @mike30 - ' You use smart pointer templates.' Smart pointer templates are certainly nice, and I make good use of them. However, Stroustrup's RAII is not talking about smart pointers! That's something that they added later on, to SOLVE THE PROBLEMS encountered when following the strict RAII paradigm. For years smart pointers were not part of the standard at all - they were only in Boost. Only 11 brought them into the standard. What does that tell you? (It tells me that RAII is unworkable as outlined-which is why no important language since C++ was ever designed to rely on RAII). – Vector May 15 '13 at 17:04
  • @Mikey. All RAII is based on stack allocation. Smart pointers are a mechanism to hook into the stack for heap allocated objects. If you use raw pointers and the new keyword then you are not using RAII. Stroustrup does not have any RAII examples using raw pointers unless he is giving an example of what RAII is not. – mike30 May 15 '13 at 17:11
  • 1
    @mike30: "Smart pointers are a mechanism to hook into the stack for heap allocated objects." Understood - it is a form of what is commonly known as "garbage collection", and is not entirely deterministic - thus we also have 'weak pointers'... Such models are also supported by MS COM, Java, C# and Python, just to name a few. So you're simply making my point: You're saying the C++ has now become more like Java and C#, thanks to smart pointers! Certainly, if a language features automated garbage collection, finally is less important, although it still has its place. – Vector May 15 '13 at 17:29
  • 1
    @Mikey. Yes using RAII feels more like using garbage collection from the perspective of the library consumer. But the point is about the differences of RAII and -finally-. RAII is implicit (in consumer code). Finally is explicit by the consumer. Is there a benefit of finally over RAII to justify including it? I only see disadvantages. – mike30 May 15 '13 at 17:45
  • 2
    @mike30 - 'I only see disadvantages.' I can't really argue with that, since I do my best to avoid using try..finally since I developed my bag of tricks in Delphi. All I'm really saying is that if you're going to allow explicit, unconstrained heap allocation in your language, as C++ does, you need a finally construct. – Vector May 15 '13 at 18:45
  • 2
    @Mikey. You're right, it would make sense to have a finally to handle raw pointers to the heap. For code written in the old style it would be useful. – mike30 May 15 '13 at 21:18
  • @mike30 - see Mason Wheeler's comment. – IceCold Dec 03 '19 at 13:06

7 Answers7

61

It's really just a matter of understanding the philosophy and idioms of C++. Take your example of an operation that opens a database connection on a persistent class and has to make sure that it closes that connection if an exception is thrown. This is a matter of exception safety and applies to any language with exceptions (C++, C#, Delphi...).

In a language that uses try / finally, the code might look something like this:

database.Open();
try {
    database.DoRiskyOperation();
} finally {
    database.Close();
}

Simple and straightforward. There are, however, a few disadvantages:

  • If the language doesn't have deterministic destructors, I always have to write the finally block, otherwise I leak resources.
  • If DoRiskyOperation is more than a single method call - if I have some processing to do in the try block - then the Close operation can end up being a decent bit away from the Open operation. I can't write my cleanup right next to my acquisition.
  • If I have several resources that need to be acquired then freed in an exception-safe manner, I can end up with several layers deep of try / finally blocks.

The C++ approach would look like this:

ScopedDatabaseConnection scoped_connection(database);
database.DoRiskyOperation();

This completely solves all of the disadvantages of the finally approach. It has a couple of disadvantages of its own, but they're relatively minor:

  • There's a good chance you need to write the ScopedDatabaseConnection class yourself. However, it's a very simple implementation - only 4 or 5 lines of code.
  • It involves creating an extra local variable - which you're apparently not a fan of, based on your comment about "constantly creating and destroying classes to rely on their destructors to clean up your mess is very poor" - but a good compiler will optimize out any of the extra work that an extra local variable involves. Good C++ design relies a lot on these sorts of optimizations.

Personally, considering these advantages and disadvantages, I find RAII (Resource Acquisition Is Initialization) a much preferable technique to finally. Your mileage may vary.

Finally, because RAII is such a well-established idiom in C++, and to relieve developers of some of the burden of writing numerous Scoped... classes, there are libraries like ScopeGuard and Boost.ScopeExit that facilitate this sort of deterministic cleanup.

Robert Harvey
  • 199,517
Josh Kelley
  • 11,051
  • 7
  • 39
  • 50
  • 8
    C# has the using statement, which automatically cleans up any object implementing the IDisposable interface. So while it's possible to get it wrong, it's pretty easy to get it right. – Robert Harvey May 09 '13 at 19:16
  • @RobertHarvey - True. I'll update my answer; thanks. – Josh Kelley May 09 '13 at 19:24
  • 24
    Having to write an entirely new class to take care of temporary state change reversal, using a design idiom that is implemented by the compiler with a try/finally construct because the compiler does not expose a try/finally construct and the only way to access it is through the class-based design idiom, is not an "advantage;" it's the very definition of an abstraction inversion. – Mason Wheeler May 09 '13 at 20:56
  • 21
    @MasonWheeler - Umm, I didn't say that having to write a new class is an advantage. I said it's a disadvantage. On the balance, though, I prefer RAII to having to use finally. Like I said, your mileage may vary. – Josh Kelley May 09 '13 at 21:01
  • 3
    So, when features are added to C++, is this based on some opinion poll of the user base of C++? Did finally actually lose out to, say, lambdas in this objective way? – Kaz May 09 '13 at 21:16
  • 2
    @Kaz I don't know how the committee decides features but generally they do not seem to add feature that are easily implemented in code. struct Finally{ Finally(std::function<void()> fn) : fn(fn){} ~Finally() { fn();} private: std::function<void()> fn;} Usage would be Finally myFinally([&](){ DoWhatever();}) – NtscCobalt May 09 '13 at 21:38
  • 9
    @JoshKelley: 'Good C++ design relies a lot on these sorts of optimizations.' Writing gobs of extraneous code and then relying on compiler optimization is Good Design?! IMO it's the antithesis of good design. Among the fundamentals of good design is concise, easily readable code. Less to debug, less to maintain, etc etc etc. You should NOT be writing gobs of code and then relying on the compiler to make it all go away - IMO that makes no sense at all! – Vector May 09 '13 at 21:56
  • 19
    @Mikey: So duplicating cleanup code (or the fact that cleanup must happen) all over the code-base is "concise" and "easily readable"? With RAII, you write such code once, and it is automatically applied everywhere. – Mankarse May 10 '13 at 01:45
  • 1
    @Mankarse : 1) INDENTATION 2) Never write a function that requires more than one try..finally block. If you find that you need to do so, 99% of the time it means refactor. – Vector May 10 '13 at 09:01
  • 4
    @Josh Kelly: You prefer having to use RAII to having to use finally? – Giorgio May 10 '13 at 12:46
  • 1
    Like the C# using statement, Java 7 has its own version of RAII: try (InputStream is = new FileInputStream("file.txt") ) { ... } – Derek Ledbetter May 10 '13 at 23:22
  • 7
    @MasonWheeler The 'extra' work amounts to being refactoring work. Any bugfix in the class benefits all use sites. Compare that to reviewing the correctness of each and every try/finally constructs -- what happens if you find a bug in one? How do you know where to look in case you made a similar error, except that the other sites may be subtly different due to how those things compose and nest? RAII is not used by C++ programmers because or despite a lack of try/finally, but for its own merits (or: I've never, ever forgotten to close anything in C++). – Luc Danton May 11 '13 at 10:48
  • 7
    another advantage of RAII is that it works for member variables as well as local variables (neither finally nor using works for members) – jk. Sep 22 '14 at 06:36
  • 1
    Always bugs me when people answer the question by changing the design instead of answering the question. Sometimes RAII is not the right way to go. – Erik Aronesty Aug 09 '18 at 17:19
  • "you need to write the ScopedDatabaseConnection yourself.it's only 4 lines of code" - Yah. And this is how you end up with 500 small classes. This is why when you see a big C++ project for the first time, you feel like the knight in the front of some dark gates ready to start The Quest. You don't know what kind of furry monsters will fall over you when you open the gates, but you know there will be plenty of them. Once you defeat the gates and enter the realm of 1000 cpp files, you navigate the entangled map with a question ringing in your brain: where TF is this class defined & what is doing? – IceCold Dec 03 '19 at 12:52
  • "only 4 lines of code" - I think in the end the amount of code is the same. In C++ you just hide it in some obscure class/file. In C#/Delphi is show on the spot what you intend to do. DON'T FORGET that you can do the same in C#/Delphi if you have many try/finally nested levels. You can take that code, make a function out of it and hide it in some obscure file/class. – IceCold Dec 03 '19 at 13:00
  • it can as well make database.Open() return a scoped variable which close the connection. nothing different from return a IDisposable (i.e. c# is not cleaner and you do need to write additional class in c#, too)
  • – apple apple Jan 13 '21 at 17:32
  • Well I think database itself should have proper destructor which close the connection (just like fstream) anyway, which eliminate the need of ScopedDatabaseConnection(which is a big concern for some people) altogether. @JoshKelley
  • – apple apple Jan 13 '21 at 17:32
  • note: which is a big concern for some people: as I see in this thread. – apple apple Jan 13 '21 at 18:20