RAII



Item 40. RAII

The C++ community has a long and proud tradition of inscrutable abbreviations and odd names for techniques. RAII manages to attain both oddness and inscrutability. RAII stands for "resource acquisition is initialization." (No, not "initialization is resource acquisition," as some would have it. If you're going to be odd, you've got to go all the way or the whole thing falls flat.)

RAII is a simple technique that harnesses C++'s notion of object lifetime to control program resources like memory, file handles, network connections, audit trails, or whatever. The basic technique is simple. If you want to keep track of an important resource, create an object and associate the resource's lifetime with the object's lifetime. In that way, you can use C++'s sophisticated object management facilities to manage resources. In its simplest form, we create an object whose constructor seizes a resource and whose destructor frees the resource.

class Resource { ... };
class ResourceHandle {
  public:
    explicit ResourceHandle( Resource *aResource )
        : r_(aResource) {} // seize resource
    ~ResourceHandle()
        { delete r_; } // release resource
    Resource *get()
        { return r_; } // access resource
  private:
    ResourceHandle( const ResourceHandle & );
    ResourceHandle &operator =( const ResourceHandle & );
    Resource *r_;
};

The nice thing about a ResourceHandle object is that, if it is declared as a local member of a function, as a function argument, or as a static, we are guaranteed to get a destructor call and recover the resource to which it refers. This is an important property if we want to keep track of our important resources in the face of slapdash maintenance or propagating exceptions. Consider some simple code that doesn't employ RAII:

void f() {                                        
    Resource *rh = new Resource;                  
    //...                                         
    if( iFeelLikeIt() ) // bad maintenance        
        return;                                   
    //...                                         
    g(); // exception?                            
    delete rh; // do we always get here?          
}                                                 

It may be that the original version of this function was safe, and the resource to which rh referred was always recovered. However, over time such code tends to break, as less experienced maintainers insert early returns, call functions that can throw an exception, or otherwise avoid the resource recovery code at the end of the function. Use of RAII results in a function that is both simpler and more robust:

void f() {
    ResourceHandle rh( new Resource );
    //...
    if( iFeelLikeIt() ) // no problem!
        return;
    //...
    g(); // exception? no problem!
    // rh destructor performs deletion!
}

The only time you're not guaranteed a destructor call when using RAII is if the resource handle is allocated on the heap, since the destructor will be called only if the object is deleted explicitly. (In the interests of full disclosure, there are also edge cases where abort or exit is called and an iffy situation that can occur if a thrown exception is never caught.)

ResourceHandle *rhp =                                               
    new ResourceHandle(new Resource); // bad idea!                  

RAII is such a pervasive technique in C++ programming that it's hard to find a library component or significant block of code that does not employ it in some fashion. Note that we have a very broad definition of "resource" that can be controlled via RAII. In addition to resources that are essentially hunks of memory (buffers, strings, container implementations, and the like), we can use RAII to control system resources like file handles, semaphores, and network connections, as well as less glamorous things like login sessions, graphical shapes, or zoo animals.

Consider the following class:

class Trace {
  public:
    Trace( const char *msg ) : msg_(msg)
        { std::cout << "Entering " << msg_ << std::endl; }
    ~Trace()
        { std::cout << "Leaving " << msg_ << std::endl; }
  private:
    std::string msg_;
};

In the case of TRace, the resource we're controlling is a message to be printed when a scope is exited. It's instructive to observe the behavior of a variety of TRace objects (automatic, static, local, and global) by following their lifetimes under various types of control flow.

void f() {
    Trace tracer( "f" ); // print "entering" message
    ResourceHandle rh( new Resource ); // seize resource
    //...
    if( iFeelLikeIt() ) // no problem!
        return;
    //...
    g(); // exception? no problem!
    // rh destructor performs deletion!
    // tracer destructor prints exiting message!
}

The code above also illustrates an important invariant of constructor and destructor structure activation: The activations form a stack. That is, we declared and initialized tracer before rh, so we are guaranteed that rh will be destroyed before tracer (last initialized, first destroyed). More generally, whenever we declare a sequence of objects, these objects will be initialized at runtime in a specific order and eventually destroyed in the inverse order. That order of destruction will not vary even in the event of an impromptu return, a propagating exception, an unusual switch, or an evil goto. (If you find this to be a dubious claim, I encourage you to play with the TRace class. Very instructive.) This property is particularly important for resource acquisition and release, since it is generally the case that resources must be seized in a particular order and released in the inverse order. For example, a network connection must be opened before an audit message is sent, and a closing audit message must be sent before the connection is closed.

This stack-based behavior extends even into the initialization and destruction of individual objects. An object's constructor initializes its base class subobjects in the order they're declared, followed by its data members in the order that they're declared. Then (and only then) is the body of the constructor executed. Now we know how the object's destructor will behave. Backward. First the destructor body is executed, then the object's data members are destroyed in the inverse order of their declaration, and finally the object's base class subobjects are destroyed in the inverse order of their declaration. In case it's not obvious by this point, this stack-like behavior is really handy for seizing and releasing an object's required resources.