RAII in C++

[This post was revised on May 23, 2005.]

Resource Acquisition Is Initialization
I first learned about the “Resource acquisition is initialization” (RAII) technique from Bjarne Stroustrup’s The C++ Programming Language (3rd Edition). RAII changed the way I write C++ code.

The meaning behind the phrase “Resource acquisition is initialization” is that acquiring a resource is performed by constructing an appropriate object. The object manages the lifetime of the resource and the object’s destructor ensures the resource is ‘un-acquired’.

A resource, for the purposes of this discussion, is something that must be explicitly acquired and released. For example, heap memory acquired by newand released by delete is a resource.

Managing the lifetime of a resource can be difficult. RAII’s power and simplicity comes from making managing the lifetime of a resource the same as managing the lifetime of an object. Managing object lifetimes is comparatively easy. The language is designed for managing objects and RAII objects are often local automatics.

Deterministic Destruction
C++ guarantees an object’s destructor will be executed when the object either goes out of scope or is deleted. Because destruction depends on either scope or when delete is invoked, it can be known when an object will be destructed. This is termed “deterministic destruction.”

Not all languages provide deterministic destruction and it’s usually essential for RAII. Why? Resources are limited and have temporal constraints. A resource that is both scarce and in high demand can’t be lazily released.

Without deterministic destruction an object that wraps a resource should expose a cleanup method and consumers of the object are responsible for calling that method. This is a very useful pattern (sometimes referred to as the “dispose pattern”) but it isn’t RAII and it introduces problems that RAII doesn’t suffer. First, because cleanup is separated from destruction, the object must guard against cleanup being performed more than once and against other methods being called after cleanup has been performed. Second, the abstraction created by the class type is compromised by requiring external knowledge that something within needs cleanup.

One-Stage Versus Two-Stage Construction
The Microsoft Foundation Class Library (MFC) is heavily invested in two-stage construction. Two-stage construction is when an object’s constructor does little or nothing and a consumer of the object must call a separate initialization method before the object is usable.

MFC uses two-stage construction because MFC was designed before exceptions became a standard part of the language. Prior to exceptions there was no mechanism for a constructor to signal a failure. Therefore it was prudent to never perform an action that could fail in a constructor.

C++ was standardized six years ago and Standard C++ includes exceptions and defines semantics for constructors that throw.

I favor one-stage construction. I want to make my job easier. I want to minimize the number of methods on a class and I want to minimize the opportunities for silly mistakes like forgetting to call an initialization method.

Two-stage done right would guard against attempts to use the object in an un-initialized state. (I want my objects to tell me when I’m being a bozo.) But guarding against un-initialized use adds complexity. (MFC classes don’t guard but also generally have no encapsulation.)

With one-stage construction the constructor either successfully creates and fully initializes the object or there is no object. It’s not possible to have an object (that enforces invariants) in an unusable state.

Exceptions And Exception Safety
There are some who will question how one-stage construction can make things easier since it depends on using exceptions and exceptions are notoriously dangerous when thrown through code that is not exception safe.

I have a short smart-alecky answer: Exception unsafe code hurts, so stop writing it.

A longer answer is probably worthy of its own blog post but the essential issue is that exceptions contaminate a codebase. If the potential for exceptions exists in a codebase (in the present or in the future) it’s prudent to assume that an exception could happen nearly anywhere and nearly all code should be written to defend against exceptions.

Exceptions are not evil and there are plenty of ways that don’t involve exceptions to create buggy leaky code. However the presence of exceptions does impact how code should be written. The good news is that most of the techniques for writing exception safe code are good practices in their own right. Which comes back to the topic at hand: RAII is indispensable for ensuring resources are not leaked when an exception is thrown and the stack is unwound.

RAII is DRY
RAII supports the DRY principle. DRY stands for “Don’t Repeat Yourself.” (See my previous post: constructive nonconformist: A Single Point of Correctness.)

An RAII class provides a single expression of how to correctly acquire and release a given resource. Creating an instance of an RAII object can replace copying and pasting boiler plate code for acquisition and release. In this way RAII protects against programmer error and reduces code clutter.

RAII Example
RAII may sound great in concept but “the proof of the pudding is in the eating.” So the following is a concrete example of using RAII.

To support critical sections, the Microsoft Windows Win32 API includes a specialized critical section synchronization object. A CRITICAL_SECTION struct must be initialized before use and deleted after use by the InitializeCriticalSection() and DeleteCriticalSection() APIs respectively. This fits the resource usage pattern of an RAII class.

Here’s an RAII class for wrapping a CRITICAL_SECTION:

class CriticalSection
{
public:
    CriticalSection()
        { ::InitializeCriticalSection(&m_rep); }
    ~CriticalSection()
        { ::DeleteCriticalSection(&m_rep); }
    
    void Enter()
        { ::EnterCriticalSection(&m_rep); }
    void Leave()
        { ::LeaveCriticalSection(&m_rep); }
    
private:
    // copy ops are private to prevent copying
    CriticalSection(const CriticalSection&);
    CriticalSection& operator=(const CriticalSection&);
    
    CRITICAL_SECTION m_rep;
};

Copying a CriticalSection object doesn’t make sense so copying is disallowed. See constructive nonconformist: Disallowing Copying in a C++ Class for details.

Another RAII class can be used to create a critical section synchronization lock.

class CSLock
{
public:
    CSLock(CriticalSection& a_section)
        : m_section(a_section) { m_section.Enter(); }
    ~CSLock()
        { m_section.Leave(); }
    
private:
    // copy ops are private to prevent copying
    CSLock(const CSLock&);
    CSLock& operator=(const CSLock&);
    
    CriticalSection& m_section;
};

Together the CriticalSection and CSLock classes can be used to synchronize thread access to the state of an object:

class Example
{
public:
    bool Process( … );
    
    …
    
private:
    CriticalSection m_criticalsection;
    
    …
};
    
bool Example::Process( … )
{
    CSLock lock(m_critsection);
    
    // do some stuff
    …
}

Only one thread at a time may acquire a critical section object. A second thread entering Example::Process() will block on the construction of the lock object until the first thread exits the function. If an exception is thrown in or through Example::Process() the lock object will still release.

By wrapping the raw Win32 structs and APIs, the RAII classes in this example allow coding at a higher level of abstraction.

Related Reading
Programming with Exceptions By Bjarne Stroustrup
Stroustrup: C++ Style and Technique FAQ: Why doesn’t C++ provide a “finally” construct?
Abstraction and Efficiency, A Conversation with Bjarne Stroustrup, Part III