views:

5345

answers:

7

Does C++ support 'finally' blocks?

What is the RAII idiom?

What is the difference between C++'s RAII idiom and C#'s 'using' statement?

+20  A: 

No, C++ does not support 'finally' blocks. The reason is that C++ instead supports RAII: "Resource Acquisition Is Initialization" -- a poor name for a really useful concept.

The idea is that an object's destructor is responsible for freeing resources. When the object is stack allocated, the object's destructor will be called whenever the object is popped off the stack -- when the object cleanly goes out of scope or during stack unwinding in the presence of an exception. Here is Bjarne Stroustrup's explanation of the topic.

A common use for RAII is locking a mutex:

// A class with implements RAII
class lock
{
    mutex &m_;

public:
    lock(mutex &m)
      : m_(m)
    {
        m.acquire();
    }
    ~lock()
    {
        m_.release();
    }
};

// A class which uses 'mutex' and 'lock' objects
class foo
{
    mutex mutex_; // mutex for locking 'foo' object
public:
    void bar()
    {
        lock scopeLock(mutex_); // lock object.

        foobar(); // an operation which may throw an exception

        // scopeLock will be destructed even if an exception
        // occurs, which will release the mutex and allow
        // other functions to lock the object and run.
    }
};

RAII also simplifies using objects in as members of other classes. When the owning class' is destructed, the resource managed by the RAII class gets released because the destructor for the RAII-managed class gets called as a result. This means that when you use RAII for all members in a class that manage resources, you can get away with a using a very simple, maybe even the default, destructor for the owner class since it doesn't need to manually manage its member resource lifetimes. (Thanks to Mike B for pointing this out.)

For those familliar with C# or VB.NET, you may recognize that RAII is similar to .NET deterministic destruction using IDisposable and 'using' statements. Indeed, the two methods are very similar. The main difference is that RAII will deterministically release any type of resource -- including memory. When implementing IDisposable in .NET (even the .NET language C++/CLI), resources will be deterministically released except for memory. In .NET, memory is not be deterministically released; memory is only released during garbage collection cycles.

 

† Some people believe that "Destruction is Resource Relinquishment" is a more accurate name for the RAII idiom.

Kevin
"Destruction is Resource Relinquishment" - DIRR... Nope, doesn't work for me. =P
Erik Forbes
RAII is stuck -- there's really no changing it. Trying to do so would be foolish. However, you have to admit though that "Resource Acquisition Is Initialization" is still a pretty poor name.
Kevin
Answering your own questions? Ubercool.
Constantin
Wtf, that's just abusive.
TraumaPony
Poor name indeed, I still can't get a hang of the name.
I agree on the name - pity we're stuck with it.
Michael Burr
SBRM == Scope Bound Resource Management
Johannes Schaub - litb
+1  A: 

FWIW, Microsoft Visual C++ does support try,finally and it has historically been used in MFC apps as a method of catching serious exceptions that would otherwise result in a crash. For example;

int CMyApp::Run() 
{
    __try
    {
     int i = CWinApp::Run();
     m_Exitok = MAGIC_EXIT_NO;
     return i;
    }
    __finally
    {
     if (m_Exitok != MAGIC_EXIT_NO)
      FaultHandler();
    }
}

I've used this in the past to do things like save backups of open files prior to exit. Certain JIT debugging settings will break this mechanism though.

Shane MacLaughlin
bear in mind that's not really C++ exceptions, but SEH ones. You can use both in MS C++ code. SEH is an OS exception handler that is the way VB, .NET implement exceptions.
gbjbaanb
and you can use SetUnhandledExceptionHandler to create a 'global' un-catched exception handler - for SEH exceptions.
gbjbaanb
+8  A: 

Beyond making clean up easy with stack-based objects, RAII is also useful because the same 'automatic' clean up occurs when the object is a member of another class. When the owning class is destructed, the resource managed by the RAII class gets cleaned up because the dtor for that class gets called as a result.

This means that when you reach RAII nirvana and all members in a class use RAII (like smart pointers), you can get away with a very simple (maybe even default) dtor for the owner class since it doesn't need to manually manage its member resource lifetimes.

Michael Burr
That's a very good point. +1 to you.Not many other people have voted you up though. I hope you don't mind that I edited my post to include your comments. (I gave you credit of course.) Thanks! :)
Kevin
+10  A: 

In C++ the finally is NOT required because of RAII.

RAII moves the responsibility of exception safety from the user of the object to the designer (and implementer) of the object. I would argue this is the correct place as you then only need to get exception safety correct once (in the design/implementation). By using finally you need to get exception safety correct every time you use an object.

Also IMO the code looks neater (see below).

Example:

A database object. To make sure the DB connection is used it must be opened and closed. By using RAII this can be done in the constructor/destructor.

C++ Like RAII

void someFunc()
{
    DB    db("DBDesciptionString");
    // Use the db object.

} // db goes out of scope and destructor closes the connection.
  // This happens even in the presence of exceptions.

The use of RAII makes using a DB object correctly very easy. The DB object will correctly close itself by the use of a destructor no matter how we try and abuse it.

Java Like Finally

void someFunc()
{
    DB      db = new DB("DBDesciptionString");
    try
    {
        // Use the db object.
    }
    finally
    {
        // Can not rely on finaliser.
        // So we must explicitly close the connection.
        try
        {
            db.close();
        }
        catch(Throwable e)
        {
           /* Ignore */
           // Make sure not to throw exception if one is already propagating.
        }
    }
}

When using finally the correct use of the object is delegated to the user of the object. i.e. It is the responsibility of the object user to correctly to explicitly close the DB connection. Now you could argue that this can be done in the finaliser, but resources may have limited availability or other constraints and thus you generally do want to control the release of the object and not rely on the non deterministic behavior of the garbage collector.

Also this is a simple example.
When you have multiple resources that need to be released the code can get complicated.

Martin York
+1  A: 

try { ... goto finally; } catch(...) { ... goto finally; } finally: { ... }

Unhandled exception
Cute idiom, but its not quite the same. returning in the try block or catch won't pass through your 'finally:' code.
Edward Kmett
+2  A: 

Sorry for digging up such an old thread, but there is a major error in the following reasoning:

RAII moves the responsibility of exception safety from the user of the object to the designer (and implementer) of the object. I would argue this is the correct place as you then only need to get exception safety correct once (in the design/implementation). By using finally you need to get exception safety correct every time you use an object.

More often than not, you have to deal with dynamically allocated objects, dynamic numbers of objects etc. Within the try-block, some code might create many objects (how many is determined at runtime) and store pointers to them in a list. Now, this is not an exotic scenario, but very common. In this case, you'd want to write stuff like

void DoStuff(vector<string> input)
{
  list<Foo*> myList;

  try
  {    
    for (int i = 0; i < input.size(); ++i)
    {
      Foo* tmp = new Foo(input[i]);
      if (!tmp)
        throw;

      myList.push_back(tmp);
    }

    DoSomeStuff(myList);
  }
  finally
  {
    while (!myList.empty())
    {
      delete myList.back();
      myList.pop_back();
    }
  }
}

Of course the list itself will be destroyed when going out of scope, but that wouldn't clean up the temporary objects you have created.

Instead, you have to go the ugly route:

void DoStuff(vector<string> input)
{
  list<Foo*> myList;

  try
  {    
    for (int i = 0; i < input.size(); ++i)
    {
      Foo* tmp = new Foo(input[i]);
      if (!tmp)
        throw;

      myList.push_back(tmp);
    }

    DoSomeStuff(myList);
  }
  catch(...)
  {
  }

  while (!myList.empty())
  {
    delete myList.back();
    myList.pop_back();
  }
}

Also: why is it that even managed lanuages provide a finally-block despite resources being deallocated automatically by the garbage collector anyway?

Hint: there's more you can do with "finally" than just memory deallocation.

Mephane
Managed languages need finally-blocks precisely because only one kind of resource is automatically managed: memory. RAII means that all resources can be handled in the same way, so no need for finally.If you actually used RAII in your example (by using smart pointers in your list instead of naked ones), the code would be simpler than your "finally"-example. And even simpler if you don't check the return value of new - checking it is pretty much pointless.
Myto
`new` doesn't return NULL, it throws an exception instead
Hasturkun
@Mephane: You raise an important question, but it does have 2 possible answers. One is that given by Myto -- use smart pointers for all dynamic allocations. The other is to use standard containers, which always destroy their contents upon destruction. Either way, every allocated object is ultimately owned by a statically allocated object which automatically frees it upon destruction. It's a real shame that these better solutions are hard for programmers to discover because of the high visibility of plain pointers and arrays.
j_random_hacker
A: 

why is it that even managed lanuages provide a finally-block despite resources being deallocated automatically by the garbage collector anyway?

Actually languages based on Garbage collectors need finally more. A garbage collector does not destrory your objects in a timely mannor and can not be relied apon to clean up non-memory related issues by itself correctly.

In terms of dynamicly allocated data, many would argue that you should be using smart-pointers.

However...

RAII moves the responsibility of exception safety from the user of the object to the designer

Sadly this is its own downfall. Old C programming habits die hard. When you're using a library written in C or a very C style, RAII won't have been used. Short of re-writing the entire API front-end, that's just what you have to work with. Then the lack of "finally" really bites.

couling