Tuesday 31 March 2009

Resource Acquisition Is Initialization

Let's take a moment away from Java to consider how C++ addresses the problem of resource handling. The usual technique is known as Resource Acquisition Is Initialization. The aspect of this pattern relevant to this discussion is that you wrap up the resource handling in a class which closes the resources in its destructor. In C++ you can declare the object as a stack variable, and thus whether the client code throws an exception or returns normally, the destructor is always called and the resource closed. In addition note that destructors cannot throw exceptions, so any exception thrown by close() would be ignored - consequently in C++ close() does not throw an exception. This can be easily achieved with an API e.g. clients calling a flush() function (which can throw) followed by close().

With these two differences from Java (controllable object lifetimes & close() not throwing exceptions) we find making exception-safe client code becomes trivial:

void foo() {
MyResourceWrapper w;
//do some stuff with w

Where we have a class:

class myResourceWrapper
{
public:
    myResourceWrapper() : r()
    {
    }
 
    ~myResourceWrapper()
    {
        r.close();
    }
 
    void bar()
    {
        //function to manipulate r...
    }
 
private:
    myResource r;
 
    // prevent copying and assignment; not implemented
    myResourceWrapper(const myResourceWrapper&);
    myResourceWrapper& operator= (const myResourceWrapper&);
};

Note that using multiple resource wrappers in a single function provides no extra complications (no equivalent to the nested try blocks) - each class that needs closing is wrapped once and then can be used in an exception-safe fashion everywhere.

No comments:

Post a Comment