12

I want to ask a question about memory leaks in constructor. Let's consider a class:

 class Foo
 {
   public:
      Foo(){ throw 500;} 
 };

What is the difference between

std::unique_ptr<Foo> l_ptr = std::make_unique<Foo>();

and

std::unique_ptr<Foo> l_ptr;
l_ptr.reset(new Foo());

In my opinion a solution with make_unique should protect me from memory leak but in both situations I got the same valgrind result:

$ valgrind --leak-check=full ./a.out
==17611== Memcheck, a memory error detector
==17611== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==17611== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==17611== Command: ./a.out
==17611== 
terminate called after throwing an instance of 'int'
==17611== 
==17611== Process terminating with default action of signal 6 (SIGABRT)
==17611==    at 0x5407418: raise (raise.c:54)
==17611==    by 0x5409019: abort (abort.c:89)
==17611==    by 0x4EC984C: __gnu_cxx::__verbose_terminate_handler() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==17611==    by 0x4EC76B5: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==17611==    by 0x4EC7700: std::terminate() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==17611==    by 0x4EC7918: __cxa_throw (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==17611==    by 0x40097B: Foo::Foo() (in /home/rungo/Repositories/test/a.out)
==17611==    by 0x4008DC: main (in /home/rungo/Repositories/test/a.out)
==17611== 
==17611== HEAP SUMMARY:
==17611==     in use at exit: 72,837 bytes in 3 blocks
==17611==   total heap usage: 4 allocs, 1 frees, 72,841 bytes allocated
==17611== 
==17611== 132 bytes in 1 blocks are possibly lost in loss record 2 of 3
==17611==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17611==    by 0x4EC641F: __cxa_allocate_exception (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==17611==    by 0x400963: Foo::Foo() (in /home/rungo/Repositories/test/a.out)
==17611==    by 0x4008DC: main (in /home/rungo/Repositories/test/a.out)
==17611== 
==17611== LEAK SUMMARY:
==17611==    definitely lost: 0 bytes in 0 blocks
==17611==    indirectly lost: 0 bytes in 0 blocks
==17611==      possibly lost: 132 bytes in 1 blocks
==17611==    still reachable: 72,705 bytes in 2 blocks
==17611==         suppressed: 0 bytes in 0 blocks
==17611== Reachable blocks (those to which a pointer was found) are not shown.
==17611== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==17611== 
==17611== For counts of detected and suppressed errors, rerun with: -v
==17611== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[1]    17611 abort (core dumped)  valgrind --leak-check=full ./a.out

It is the same when I use clang++ and g++. I found here: https://isocpp.org/wiki/faq/exceptions#ctors-can-throw the sentence:

Note: if a constructor finishes by throwing an exception, the memory associated with the object itself is cleaned up — there is no memory leak.

My question is why we have a leak in this situation and why make_unique is not preventing a leak (doeas it means that there is no dofference between make_unique and reset(new ...)?

rungus2
  • 277
  • 4
  • 11

3 Answers3

10

Are you catching the exception? When catching the exception, valgrind (compiling with g++ 6.2 -g) detects no leaks both with make_unique and reset.

int main() try
{
#if TEST_MAKE_UNIQUE
    std::unique_ptr<Foo> l_ptr = std::make_unique<Foo>();   
#else
    std::unique_ptr<Foo> l_ptr;
    l_ptr.reset(new Foo());
#endif
}
catch(...)
{

}

AddressSanitizer does not report any issue as well.

(P.S. this was a nice opportunity to show off the less known function-try-block language feature.)


"Why doesn't the memory get leaked?"

The standard guarantees that the memory allocated with a "new expression" will be automatically freed if an exception is thrown during construction.

From $15.2.5:

If the object was allocated by a new-expression ([expr.new]), the matching deallocation function ([basic.stc.dynamic.deallocation]), if any, is called to free the storage occupied by the object.


Related questions:

Community
  • 1
  • 1
Vittorio Romeo
  • 82,972
  • 25
  • 221
  • 369
  • Catching exception helped with valgrind but my question is about difference between this two methods? Are they equal in this situation? – rungus2 Dec 06 '16 at 09:26
  • 5
    You might want to mention that the standard explicitly allows stack unwinding *not* to happen when an exception remains uncaught. – Angew is no longer proud of SO Dec 06 '16 at 09:27
  • @rungus2: Can you please update your question to ask what you mean to ask then? – Kerrek SB Dec 06 '16 at 09:35
  • I want to know difference between make_unique and reset(new T).I thought make_unique is a solution for memory leaks but it seems not to be true – rungus2 Dec 06 '16 at 13:35
  • 1
    Angew is right, if the exception isn't going to be caught what happens is often a call to `abort()` which is good for debugging but does not clean up anything. – Zan Lynx Dec 06 '16 at 18:04
3

make_unique can be used to avoid a memory leak in certain circumstances, e.g.:

int foo();
void bar(unique_ptr<int> a, int b);

int main()
{
    try
    {
         bar(unique_ptr<int>(new int(5)), foo());
    }
    catch (...) {/*TODO*/}
}

It is possible here for the call to new to happen first, and the call to foo() then may happen before the unique_ptr<int> construction. If foo() throws, then the int will be leaked. This is not possible if make_unique is used instead:

int foo();
void bar(unique_ptr<int> a, int b);

int main()
{
    try
    {
        bar(make_unique<int>(5), foo());
    }
    catch (...) {/*TODO*/}
}

In this case, either foo() is called first, and no int is created if it throws, or make_unique<int>(5) is called first, and allowed to finish. If foo() then throws, the int will be deleted during stack unwinding via the destructor of the temporary unique_ptr<int>.

If you don't put anything else that can throw inside the same statement, as with the call to l_ptr.reset(new Foo());, then make_unique does not improve safety. It may still be more convenient, though.

If you don't catch a thrown exception, the stack may or may not be unwound. In a multi-threaded program, you can even trigger undefined behavior by letting exceptions "escape" from a thread. In short, don't do that.

UPDATE In C++17, the above bar(unique_ptr<int>(new int(5)), foo()); is also exception safe, because the evaluation of function arguments is no longer unsequenced but now indeterminately sequenced. This means the compiler has to guarantee that there is an order, it just doesn't have to tell you which order. See Barry's response to this question.

Arne Vogel
  • 5,773
  • 2
  • 16
  • 29
  • It looks like you are saying the evaluation of one argument in a function call can happen in the _middle_ of the evaluation of another argument in a function call. Is that actually allowed by the standard? – Francis Litterio Dec 06 '16 at 21:17
  • 2
    Yes, it is allowed. The order in which side effects from function argument evaluations occur is unspecified (they have to be finished before entering the function). Sorry for the late reply. See https://stackoverflow.com/questions/2934904/order-of-evaluation-in-c-function-parameters – Arne Vogel May 24 '17 at 16:03
1

As mentioned in other responses, valgrind is complaining that you're leaking memory as the result of having an uncaught exception, which in turn calls std::terminate, which in turn leaves everything as it is. If the executable does not terminate (e.g., you catch the exception somewhere), the memory will be freed automatically by the language's definition of how new behaves.

It seems like you're asking a deeper question about why std::make_unique exists. This exists for a situation like:

some_func(new Foo, new Foo);

In just such a case, the language makes no guarantees about when new gets called vs. when Foo::Foo() gets called. You may have the compiler organize things a bit such that new gets called twice, once to allocate space for the first parameter, and once to allocate space for the second parameter. Then Foo() gets constructed, throws an exception. The first allocation gets cleared up, but the second one will have leaked because there was no construction (or exception there)! Queue make_unique:

some_func(std::make_unique<Foo>(), std::make_unique<Foo>());

Now that we are calling a function, the new will be called before the constructor for each parameter in that order. If the first parameter throws an exception, the second allocation won't even have happened.

Vs. unique_ptr::reset we just have convenience. You should not see any issues with memory leaks because of the defined new behavior with throwing exceptions.

cyberbisson
  • 332
  • 2
  • 8