C++ RAII

What is RAII

RAII is an acronym for Resource Acquisition Is Initialization, and it’s a really useful feature of C++ programming language.

The Problem

Suppose we have the following situation:


void doSomethingUseful(void) {
    
    int* anInteger = new int;
    
    doAnotherUsefulThing();
    
    delete anInteger;
}

we are dynamically allocating an integer, performing some useful computation (maybe using the resource we first allocated), then when we’re finished, we cleanup the resource.

The problem arises if somewhere in between the resource allocation and its cleanup, perhaps in the call to the function doAnotherUsefulThing(), an exception arises, halting current computation flow and preventing it to reach the delete call we setup to cleanup the integer allocation. In this scenario the resource is never cleared, causing a memory leak. This problem is known as the Exception Safety problem.

The Solution

To solve the above problem, C++ compiler introduces the concept of RAII, which basically states that: C++ compiler guarantees that destructor of objects allocated on the stack gets called even if an exception in thrown in the body of the function that allocated them.

This means that if we wrap our resource in an object, using its constructor to allocate the resource and its destructor to cleanup it we can be sure the resource will be cleaned up even if an exception is thrown somewhere deep in our code.

The following code shows an example:

#include <iostream>

// this class defines a dynamically allocated integer using RAII to handle its cleanup in class's destructor;
class DynamicInteger {
    
    int* theIntegerRawPointer;
    
    public:
    
    // RAII constructor: allocates the resource
    DynamicInteger() {
        std::cout << "allocating the resource" << std::endl;
        theIntegerRawPointer = new int;
    }
    
    // RAII destructor: cleans the resource
    ~DynamicInteger() {
        std::cout << "cleaning up the resource" << std::endl;
        delete theIntegerRawPointer;
    }
    
    // methods to actually use the resource we are managing
    void set(int value) {
        *theIntegerRawPointer = value;
    }
    
    int get(void) {
        return *theIntegerRawPointer;
    }
    
};

With the DynamicInteger class we defined above, we can then be sure that even if an exception is thrown in the middle of the computation, the resource will be cleaned up as long as we are allocating the managing object (i.e. the DynamicInteger class) on the stack:


void thisFunctionCallWillRainAnException(void) {

    throw std::runtime_error("error");

}

int doSomethingWithTheResource(void) {
    
    // allocates the managing class on the stack
    DynamicInteger dynamicInteger;
    
    dynamicInteger.set(42);
    
    int value = dynamicInteger.get();
    
    thisFunctionCallWillRainAnException();
    
    return value;
}

int main(void) {

    try {
        int result = doSomethingWithTheResource();
        
        std::cout << "result: " << result << std::endl;
    }
    catch(...) {
        std::cout << "exception handler" << std::endl;
    }
    
}

The above approach can be used for any kind of resource that needs an allocation phase and then a cleanup phase: file pointers, memory or database connections for example.

The following code puts all pieces together:

#include <iostream>
#include <stdexcept>
#include <string>

// this class defines a dynamically allocated integer using RAII to handle its cleanup in class's destructor
class DynamicInteger {
    
    int* theIntegerRawPointer;
    
    public:
    
    // RAII constructor: allocates the resource
    DynamicInteger() {
        std::cout << "allocating the resource" << std::endl;
        theIntegerRawPointer = new int;
    }
    
    // RAII destructor: cleans the resource
    ~DynamicInteger() {
        std::cout << "cleaning up the resource" << std::endl;
        delete theIntegerRawPointer;
    }
    
    // methods to actually use the resource we are managing
    void set(int value) {
        *theIntegerRawPointer = value;
    }
    
    int get(void) {
        return *theIntegerRawPointer;
    }
    
};

void thisFunctionCallWillRaiseAnException(void) {

    throw std::runtime_error("error");

}

int doSomethingWithTheResource(void) {
    
    // allocates the managing class on the stack
    DynamicInteger dynamicInteger;
    
    dynamicInteger.set(42);
    
    int value = dynamicInteger.get();
    
    thisFunctionCallWillRaiseAnException();
    
    return value;
}

int main(void) {

    try {
        int result = doSomethingWithTheResource();
        
        std::cout << "result: " << result << std::endl;
    }
    catch(...) {
        std::cout << "exception handler" << std::endl;
    }
    
}

By executing the above code we get the following output:

allocating the resource
cleaning up the resource
exception handler

As we expect, the resource is allocated and freed even if an exception is raised thanks to the use of RAII.

Be aware of the try/catch block

Using the above example everythings works as expected: we allocate the resource and it gets freed whether an exception is thrown or not thanks to the RAII feature of the C++ compiler.

It is worth to note that for everything work as expected, we need to wrap the exception unsafe code with a try/catch block: if we remove the block from the main() and execute the code leaving everything else untouched we get the following output:

allocating the resource
terminate called after throwing an instance of 'std::runtime_error'
  what():  error
Aborted

This means that the resourse isn’t freed even if we setup a RAII manager object for it.

So we need to be aware of the exception throwing code we are using and wrap it in a try/catch block for our RAII managed resources to work as expected.

Written on February 25, 2018