Say goodbye to memory leaks in C++ with Boost!

By , last updated September 8, 2019

Many years ago Boost came to me when I started in a new job just out of Uni, and I got impressed from day one. Not only because in my previous experience Boost gave incredibly large errors but what Boost really is and what the collection of libraries can do to easily avoid many of the pitfalls with C++ programming.

Obtaining Boost is easy, simple and free. Boost’s license explicitly states it’s free as in free beer, and you can use Boost in commercial applications without breaking the license agreement.

How to build Boost

Most libraries in Boost are header only, so you don’t have to build Boost to get started with it. However, some libraries requires to be built.

Today Boost is under active development and new versions come up all the time. Here are the latest guides on how to build Boost on Windows.

C++ memory leak example

/*
 * C++ example with a memory leak in an exception
 */

#include <iostream>

void throwErrorWithMemLeak()
{
    try
    {
        // Allocate 1kB buffer
        char *buffer = new char[1024];

        // Throw
        throw 1;

        // Free up buffer
        delete buffer;
    }
    catch(const int e)
    {
        // printf("We have a memory leakn");
    }
}

int main(int args, char **argc)
{
    // A simple case of when an exception leads to a memory leak

    // Iterate the throwing function 1024 times, creating a 1MB memeory leak
    for (size_t i=0; i<1024; i++)
    {
        throwErrorWithMemLeak();
    }

    return 0;
}

Compile with

g++ -ggdb3 ex1_memleak.cpp -o ex1_memleak

And run it with valgrind to see the memory leak in action.

valgrind ./ex1_memleak --leak-check=full
==21831== Memcheck, a memory error detector.
==21831== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==21831== Using LibVEX rev 1854, a library for dynamic binary translation.
==21831== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==21831== Using valgrind-3.3.1, a dynamic binary instrumentation framework.
==21831== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==21831== For more details, rerun with: -v
==21831==
==21831==
==21831== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 1)
==21831== malloc/free: in use at exit: 1,048,576 bytes in 1,024 blocks.
==21831== malloc/free: 2,048 allocs, 1,024 frees, 1,167,360 bytes allocated.
==21831== For counts of detected errors, rerun with: -v
==21831== searching for pointers to 1,024 not-freed blocks.
==21831== checked 158,760 bytes.
==21831==
==21831== LEAK SUMMARY:
==21831==    definitely lost: 1,048,576 bytes in 1,024 blocks.
==21831==      possibly lost: 0 bytes in 0 blocks.
==21831==    still reachable: 0 bytes in 0 blocks.
==21831==         suppressed: 0 bytes in 0 blocks.

As you can see, we’ve lost 1 megabyte of memory into the void. If this were a long running program, even small memory leaks could be disastrous if it’s allowed to grow unlimited.

No memory leak Memory leak with boost scoped_ptr

Read also: Theory behind boost scoped_ptr vs unique_ptr.

/*
 * C++ example without a memory leak in an exception
 */

#include <iostream>

#include <boost/scoped_ptr.hpp>

void throwErrorWithMemLeak()
{
    try
    {
        // Allocate 1kB buffer in an scoped pointer
        boost::scoped_ptr<char> buffer( new char[1024] );

        // Throw
        throw 1;

        // Free up buffer (we really don't need this)
        // scoped_ptr.reset( 0 );
    }
    catch(const int e)
    {
    }
}

int main(int args, char **argc)
{
    // A simple case of when an exception doesn't lead to a memory leak

    // Iterate the throwing function 1024 times, creating a possible 1MB
    // memory leak, but scoped_ptr prevents it gracefully.
    for (size_t i=0; i<1024; i++)
    {
        throwErrorWithMemLeak();
    }

    return 0;
}

Compile with

g++ -ggdb3 -I../boost_1_37_0/ ex1_no_memleak.cpp -o ex1_no_memleak

Run with valgrind

valgrind ./ex1_no_memleak
==21902== Memcheck, a memory error detector.
==21902== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==21902== Using LibVEX rev 1854, a library for dynamic binary translation.
==21902== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==21902== Using valgrind-3.3.1, a dynamic binary instrumentation framework.
==21902== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==21902== For more details, rerun with: -v
==21902==
==21902== ERROR SUMMARY: 1024 errors from 1 contexts (suppressed: 4 from 1)
==21902== malloc/free: in use at exit: 0 bytes in 0 blocks.
==21902== malloc/free: 2,048 allocs, 2,048 frees, 1,167,360 bytes allocated.
==21902== For counts of detected errors, rerun with: -v
==21902== All heap blocks were freed -- no leaks are possible.

No memory leaks!

Edit:

There is actually an hidden memory leak in the above sample. Running valgrind with -v, gives a more detailed view.

valgrind -v ./scoped

==11920== 1024 errors in context 3 of 3:
==11920== Mismatched free() / delete / delete []
==11920==    at 0x4C2AB23: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==11920==    by 0x400B20: void boost::checked_delete<char>(char*) (checked_delete.hpp:34)
==11920==    by 0x400B06: boost::scoped_ptr<char>::~scoped_ptr() (scoped_ptr.hpp:82)
==11920==    by 0x400A11: throwErrorWithMemLeak() (scoped.cpp:11)
==11920==    by 0x400A60: main (scoped.cpp:29)
==11920==  Address 0x5a27040 is 0 bytes inside a block of size 1,024 alloc'd
==11920==    at 0x4C2B43B: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==11920==    by 0x4009C8: throwErrorWithMemLeak() (scoped.cpp:8)
==11920==    by 0x400A60: main (scoped.cpp:29)

Correct sample with no leak

The problem with the sample above, was that it used scoped_ptr to allocate an array with new[1024]. You must use scoped_array if you allocate an array. The same logic goes for both shared_ptr and shared_array.

#include <boost/scoped_array.hpp>

void throwErrorWithMemLeak()
{
    try
    {
        // Allocate 1kB buffer in an scoped pointer
        boost::scoped_array<char> buffer( new char[1024] );

        // Throw
        throw 1;

    }
    catch(const int e)
    {
    }
}

int main(int args, char **argc)
{
    // A simple case of when an exception doesn't lead to a memory leak

    // Iterate the throwing function 1024 times, creating a possible 1MB
    // memory leak, but scoped_array prevents it gracefully.
    for (size_t i=0; i<1024; i++)
    {
        throwErrorWithMemLeak();
    }

    return 0;
}

This is only one small fraction of Boost, but yet a very powerful one.

Professional Software Developer, doing mostly C++. Connect with Kent on Twitter.