Automatic resource management in C++

Overview

C++ doesn't have any special keyword for automatic memory / resource deallocation like Java, C#, JavaScript, Delphi, Common Lisp... Instead, you should be aware that there is a special technique - RAII - which supports this feature (see below). There were several library solutions which use this technique to implement more generic solution - like Boost.ScopeExit or Facebook Folly ScopeGuard, but usually they are implemented in complicated way or usage syntax is awkward.

In C++11 you have more possibilities - you can define own or use already implemented finally() function - see below.

Automatic memory deallocation

C++ includes in standard library special classes which make it easy to perform automatic memory deallocation. They are called in general "smart pointers" and can be divided in the following categories:

  • C++98 smart pointers: std::auto_ptr, do not use in containers in any case
  • C++11 smart pointers: std::shared_ptr, std::unique_ptr, etc., see Dynamic memory management

Boost library also implements some smart pointers for C++98 and above: shared_ptr, intrusive_ptr, scoped_ptr, weak_ptr etc.

Resources

RAII - not only memory

Every object allocated on stack in C++ is guaranteed to be deallocated, even if exception occurs. So in function:

bool ValueQueue::peek(MyStruct &output)
{
    boost::mutex::scoped_lock lck(m_mutex);

    //tag:1
    bool res = !m_data.empty();
    if (res) {
        m_data.getElement(0, output);
    }

    return res;
}

even if an exception will be thrown in lines below "tag:1" then "lck", object's destructor will be executed and the object itself will be automatically deallocated. So if you need special processing to be performed at the exit from block, you can use object destructor to perform it.

In addition to standard destructor purpose (memory allocation) they can perform other processing as well. Below you can find example of destructor performing custom operation required to be performed independently if exception occurred or not:

void foo_raii(int &counter) {
    class DecreaseOnExit {
        int &counterCnt_;

      public:    
        DecreaseOnExit(int &counterCnt) :counterCnt_(counterCnt) { }

        ~DecreaseOnExit() {
            // --> this will be performed even if exception occurs
            counterCnt_--;
        }
    };

    DecreaseOnExit b(counter);

    counter++;

    // perform processing
    boo(1,2);

    // here, on exit, counter will be decreased automatically and b object will be deallocated 
    // even if exception will occur inside boo function.
}

Resources

finally() function

Only recently I found a clean & short solution which I recommend in C+11 and above: finally() function in GSL, solution promoted by Bjarne Stroustrup in C++ Core Guidelines. It can be used with lambdas and probably with other functors.

Example of usage (implements the same processing as foo_raii, uses lambda function):

#include "gsl.h"

int foo_finally(int &counter) {
    counter++;
    finally([&] { counter--; });
    boo(1, 2);
    return counter;
}

Resources

  • GSL Lite - implementation of GSL for C++98 and up.

Other resources

See also

Share

follow