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

By , last updated September 29, 2016

Intro

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 like lang lang pelikan, 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.


Lang lang pelikan

This is the longest and ugliest c++ error that I have seen in my life. It comes from boost:

Error    2    error LNK2019: unresolved external symbol "class std::auto_ptr<class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::set<class openvrml::node_interface,struct openvrml::node_interface_compare,class std::allocator<class openvrml::node_interface> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::set<class openvrml::node_interface,struct openvrml::node_interface_compare,class std::allocator<class openvrml::node_interface> > > > > > __cdecl openvrml::profile(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?profile@openvrml@@YA?AV?$auto_ptr@V?$map@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$set@Vnode_interface@openvrml@@Unode_interface_compare@2@V?$allocator@Vnode_interface@openvrml@@@std@@@2@U?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$set@Vnode_interface@openvrml@@Unode_interface_compare@2@V?$allocator@Vnode_interface@openvrml@@@std@@@2@@std@@@2@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@@Z) referenced in function "public: void __thiscall openvrml::vrml97_grammar<struct openvrml::null_vrml97_parse_actions,struct openvrml::vrml97_parse_error_handler>::definition<class boost::spirit::scanner<class boost::spirit::position_iterator<class std::_String_iterator<char,struct std::char_traits<char>,class std::allocator<char> >,struct boost::spirit::file_position_base<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct boost::spirit::nil_t>,struct boost::spirit::scanner_policies<class boost::spirit::skip_parser_iteration_policy<struct openvrml::vrml97_skip_grammar,struct boost::spirit::iteration_policy>,struct boost::spirit::match_policy,struct boost::spirit::action_policy> > >::on_scene_start_t::operator()<class boost::spirit::position_iterator<class std::_String_iterator<char,struct std::char_traits<char>,class std::allocator<char> >,struct boost::spirit::file_position_base<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct boost::spirit::nil_t> >(class boost::spirit::position_iterator<class std::_String_iterator<char,struct std::char_traits<char>,class std::allocator<char> >,struct boost::spirit::file_position_base<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct boost::spirit::nil_t>,class boost::spirit::position_iterator<class std::_String_iterator<char,struct std::char_traits<char>,class std::allocator<char> >,struct boost::spirit::file_position_base<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct boost::spirit::nil_t>)const " (??$?RV?$position_iterator@V?$_String_iterator@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@U?$file_position_base@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@spirit@boost@@Unil_t@45@@spirit@boost@@@on_scene_start_t@?$definition@V?$scanner@V?$position_iterator@V?$_String_iterator@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@U?$file_position_base@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@spirit@boost@@Unil_t@45@@spirit@boost@@U?$scanner_policies@V?$skip_parser_iteration_policy@Uvrml97_skip_grammar@openvrml@@Uiteration_policy@spirit@boost@@@spirit@boost@@Umatch_policy@23@Uaction_policy@23@@23@@spirit@boost@@@?$vrml97_grammar@Unull_vrml97_parse_actions@openvrml@@Uvrml97_parse_error_handler@2@@openvrml@@QBEXV?$position_iterator@V?$_String_iterator@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@U?$file_position_base@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@spirit@boost@@Unil_t@45@@spirit@boost@@0@Z)    moc_qtdifi.obj   

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

Comments

  1. Fatman June 8, 2009 Leave a Reply

    I love Boost too, but in my testing I have found some of the more complicated modules (thread and regex) to be intermittently leaky. At least I’m pretty sure they’re not my leaks. Valgrind indicates they’re not.

    I’m using 1.37 (Ubuntu hasn’t caught up to 1.39 yet) so maybe these leaks have been fixed already. 🙂

    Some examples of how easy thread and regex are:


    namespace b = boost;
    ...
    MyObj obj;
    b::thread t = b::thread(b::bind(&MyObj::thread, &obj)); // spawn new thread
    b::thread t2 = b::thread(b::bind(&MyObj::thread, &obj)); // another, why not?
    ...
    t.join(); // wait for thread to finish
    t2.join(); // wait for second thread to finish


    namespace b = boost;
    ...
    std::string buffer = "testingtestingZZ11 1ZZtestingtesting";
    const b::regex fmt("[A-Z]{1,2}[0-9][0-9A-Z]? [0-9][ABDEFGHJLNPQRSTUWXYZ]{2}"");
    b::smatch what;
    if (b::regex_search(buffer, what, fmt) {
    std::cout << "Found UK postcode: " << what[0] << std::endl;
    }
    ...

    Amazing!

  2. Laks May 8, 2013 Leave a Reply

    Using boost is OK for checking memory leak but very bad for performance. Boost creates umpteen objects internally to track the references and auto release of memory

  3. Du Bois February 26, 2014 Leave a Reply

    Hi,

    I think you say something wrong. You shall use boost::scoped_array p(new char[1024]); insted of using boost::scoped_ptr p(new char[1024]); otherwise, the destructor ‘delete _p;’ is called instead of ‘delete [] _p;’

    • kent February 27, 2014 Leave a Reply

      Hi Du Bois,

      Of course you are correct. I’ve updated the article with scoped_array.

      Thanks for letting us know!

Leave a Reply


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*