tags:

views:

833

answers:

5

Bjarne Stroustrup writes in his C++ Style and Technique FAQ, emphasis mine:

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. For example:

class File_handle {
    FILE* p;
public:
    File_handle(const char* n, const char* a)
        { p = fopen(n,a); if (p==0) throw Open_error(errno); }
    File_handle(FILE* pp)
        { p = pp; if (p==0) throw Open_error(errno); }

    ~File_handle() { fclose(p); }

    operator FILE*() { return p; }

    // ...
};

void f(const char* fn)
{
    File_handle f(fn,"rw"); // open fn for reading and writing
    // use file through f
}

In a system, we need a "resource handle" class for each resource. However, we don't have to have an "finally" clause for each acquisition of a resource. In realistic systems, there are far more resource acquisitions than kinds of resources, so the "resource acquisition is initialization" technique leads to less code than use of a "finally" construct.

Note that Bjarne writes "almost always better" and not "always better". Now for my question: What situation would a finally construct be better than using the alternative construct (RAII) in C++?

+2  A: 

Finally would be better when connecting with C code. It can be a pain to have to wrap existing C functionality in RAII.

Chris Jefferson
+3  A: 

The only reason I can think of that a finally block would be "better" is when it takes less code to accomplish the same thing. For example, if you have a resource that, for some reason doesn't use RIIA, you would either need to write a class to wrap the resource and free it in the destructor, or use a finally block (if it existed).

Compare:

class RAII_Wrapper
{
    Resource *resource;

public:
    RAII_Wrapper() : resource(aquire_resource()) {}

    ~RAII_Wrapper() {
        free_resource(resource);
        delete resource;
    }

    Resource *getResource() const {
        return resource;
    }
};

void Process()
{
    RAII_Resource wrapper;
    do_something(wrapper.resource);
}

versus:

void Process()
{
    try {
        Resource *resource = aquire_resource();
        do_something(resource);
    }
    finally {
        free_resource(resource);
        delete resource;
    }
}

Most people (including me) would still argue that the first version is better, because it doesn't force you to use the try...finally block. You also only need to write the class once, not duplicate the code in every function that uses the resource.

Edit: Like litb mentioned, you should use an auto_ptr instead of deleting the pointers manually, which would simplify both cases.

Matthew Crumley
minor typo. should be RIIA_Resource wrapper; instead of RIIA_Resource wrapper(); . you can do auto_ptr<Resource> const p(aquire_resource()); auto_ptr cares about all the stuff itself. i understand that was just an example tho :) so after you fix the typo and mention a smart pointer, you get +1 :)
Johannes Schaub - litb
Oops. when I started writing it, i passed the resource into the constructor and it was stored in a reference instead of a pointer. Good catch.
Matthew Crumley
Should be RAII, not RIIA.
dalle
Apparently my class uses the "Resource Initialization Is Allocation" pattern. Thanks.
Matthew Crumley
Well, it works for me, as long as it isn't RIAA. ;)(Resource Initialization And Allocation, of course)
dalle
Also - look up scope_guard - declared once, it allows you to set an arbitrary function to be called on leaving scope.
Eclipse
+4  A: 

The difference between them is that destructors emphasise reuse of the cleanup solution by associating it with the type being used, whereas try/finally emphasises one-off cleanup routines. So try/finally is more immediately convenient when you have a unique one-off cleanup requirement associated with the point of use, rather than a reusable cleanup solution that can be associated with a type you're using.

I haven't tried this (haven't downloaded a recent gcc for months), but it should be true: with the addition of lambdas to the language, C++ can now have the effective equivalent of finally, just by writing a function called try_finally. Obvious usage:

try_finally([]
{
    // attempt to do things in here, perhaps throwing...
},
[]
{
    // this always runs, even if the above block throws...
}

Of course, you have to write try_finally, but only once and then you're good to go. Lambdas enable new control structures.

Something like:

template <class TTry, class TFinally>
void try_finally(const TTry &tr, const TFinally &fi)
{
    try
    {
        tr();
    }
    catch (...)
    {
        fi();
        throw;
    }

    fi();
}

And there is no link at all between the presence of a GC and a preference for try/finally instead of destructors. C++/CLI has destructors and GC. They're orthogonal choices. Try/finally and destructors are slightly different solutions to the same problem, both deterministic, needed for non-fungible resources.

C++ function objects emphasise reusability but make one-off anonymous functions painful. By adding lambdas, anonymous code blocks are now easy to make, and this avoids C++'s traditional emphasis on "forced reusability" expressed through named types.

Daniel Earwicker
Nice idea to implementing finally without the language construct. But try_finally should call fi() even if tr() doesn't throw.
dalle
Also moved some text to the top so it actually answers the question as well.
Daniel Earwicker
A: 

Edit after six answers.

What about this one:

class Exception : public Exception { public: virtual bool isException() { return true; } };
class NoException : public Exception { public: bool isException() { return false; } };


Object *myObject = 0;

try
{
  try
  {
    myObject = new Object(); // Create an object (Might throw exception)
  }
  catch (Exception &e)
  {
    // Do something with exception (Might throw if unhandled)
  }

  throw NoException();
}
catch (Exception &e)
{
  delete myObject;

  if (e.isException()) throw e;
}
TomWij
If your catch expression throws another exception then delete will never be called.
Kibbee
Object *myObject = 0;try{ myObject = new Object(); // Throw exception}catch (exception Problem solved :-)
Greg Dean
Because, in most cases, you do NOT want to catch the exception. And if you do catch it, you should almost always be rethrowing it.
Roddy
That's probably the ugliest hack of programming I've ever seen. Can you even do typeof in C++?
Kibbee
It is indeed ugly, and besides that RTTI calls (which you can do in C++) are very expensive so this isn't a solution either I think. Maybe you can put something in the what() function, that will make it more performant, but still ugly. :P
TomWij
Well now, I've never seen a post that shows quite so elegantly why the C++ RAII idiom is such a good idea...
Roddy
What if something else is thrown? I think this isn't a solution, or even a workaround.
dalle
+3  A: 

I think that scope guard does a good job at handling the one-off cases that finally handles well, while being better in the more general sense because it handles more than one flow path well.

Greg Rogers