1

According to valgrind the following code does not contain memory leaks:

#include <memory>
#include <stdexcept>

namespace {

class Foo {
 public:
  Foo();
};

Foo::Foo() { throw std::runtime_error("This is an error"); }

}  // anonymous namespace

int main(int argc, char* argv[]) {
  try {
    new Foo();
  } catch (const std::exception& aError) {
    return -1;
  }

  return 0;
}

Indeed. the result of valgrind --leak-check=full is:

==25913== Memcheck, a memory error detector
==25913== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==25913== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==25913== Command: ./test
==25913== 
==25913== 
==25913== HEAP SUMMARY:
==25913==     in use at exit: 0 bytes in 0 blocks
==25913==   total heap usage: 4 allocs, 4 frees, 72,890 bytes allocated
==25913== 
==25913== All heap blocks were freed -- no leaks are possible
==25913== 
==25913== For counts of detected and suppressed errors, rerun with: -v
==25913== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

The program is compiled with g++ (GCC) 7.3.0.

Note: there is no leak even with clang 5.0.1.

Note: I checked the disassembly code and there is delete call.


My question: why this code does not produce memory leak? Is it really something specified in the standard (can you link?) or just autonomous compiler enhancement?


What I expect:

...
call operator new(unsigned long)
mov rbx, rax
mov rdi, rbx
call (anonymous namespace)::Foo::Foo()

The dynamic allocation happens, after the call to the constructor which throws producing leak.

BiagioF
  • 9,368
  • 2
  • 26
  • 50
  • `Foo()` throws, so the idea is that *before* the `unique_ptr` is constructed, the constructor would throw. So you allocate memory, but don't pass it to the `unique_ptr`, which should mean that it leaks. @John3136 – Justin Feb 20 '18 at 22:57
  • @John3136 A) *As I've written*, **even without smart pointer** (a simple `new`) produces the **same** result. B) You have no idea how `smart pointers` are built. (see why standard introduces `std::make_unique`, instead of use `new`). Please be careful before to comment – BiagioF Feb 20 '18 at 22:59
  • 3
    Maybe you should change your question to not mention smart pointers, it's pretty confusing how you seem to make it all about smart pointers and then mention in a note that actually it has nothing to do with it. – M.M Feb 20 '18 at 23:03
  • 1
    'My question: why this code does not produce memory leak?' Questions of that type can be translated, "Guess what my misconception is, and refute it." – Jive Dadson Feb 20 '18 at 23:04
  • Well, smart pointer does not mean *no memory leaks*. Anyway, I can easily change the question avoiding them – BiagioF Feb 20 '18 at 23:09
  • @JiveDadson Yeah, what's wrong with that? I know there is something that I am missing and I am asking for that piece of knowledge, simple as that. – BiagioF Feb 20 '18 at 23:11
  • @BiagioFesta: _"Well, smart pointer does not mean no memory leaks."_ It should. – Lightness Races in Orbit Feb 20 '18 at 23:54
  • @BiagioFesta: FWIW, Jive was complaining about the "guess" part. We can't tell you what's wrong with your idea until we know what that idea is. A lot of people just say "why does this code do _X_?", forgetting to tell us what they expected instead. Although in this particular case I think it's pretty obvious from the question, so... – Lightness Races in Orbit Feb 20 '18 at 23:55
  • It's impossible for this code to leak a `Foo` since no `Foo` is ever successfully created. I'm puzzled what someone would even think it could potentially leak. – David Schwartz Feb 21 '18 at 00:05

1 Answers1

6

In case the constructor throws an exception, the memory will not leak, because an allocator must deallocate the memory before passing the exception, if there is a appropriate deallocation function.

According to the March 2017 Working Draft:

8.3.4 New [expr.new]

20 If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (15.5), and the constructor (15.1). If the new-expression creates an array of objects of class type, the destructor is potentially invoked (15.4).

21 If any part of the object initialization described above80 terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object’s memory to be freed. [ Note: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. — end note ]

80) This may include evaluating a new-initializer and/or calling a constructor

Also, according to the Exceptions FAQ in the C++ wiki:

“But throwing an exception from a constructor invoked by new causes a memory leak!” Nonsense! That’s an old-wives’ tale caused by a bug in one compiler – and that bug was immediately fixed over a decade ago.

Firefrost
  • 76
  • 1
  • 2