0
fn(unique_ptr<A>{new A{}},unique_ptr<B>{new B{}});

is troublesome as A or B gets leaked if an exception is thrown there.

fn(make_unique<A>(),make_unique<B>());

on the other hand is considered exception-safe.

Question:

Why? How does use of make_unique prevent memory-leak?
What does make_unique do that the default unique_ptr does not?

Kindly help me out, please.
Thank you.

Supreeto
  • 198
  • 9
  • make_unique has to do with exception safety. This link should give you all the info you're looking for: https://www.learncpp.com/cpp-tutorial/stdunique_ptr/. Next to "prevent memory leaks" std::unique_ptr models another important concept : ownership, unlinke raw_pointers you can see who's supposed to cleanup. – Pepijn Kramer Jan 09 '22 at 08:01
  • A quick search goes a long way. In addition to the duplicate question, also eg. https://www.py4u.net/discuss/67252 — make_unique ultimately creates a unique_ptr so it doesn’t “prevent a memory leak” any differently per se. – user2864740 Jan 09 '22 at 08:10

1 Answers1

6

How does use of make_unique prevent memory-leak, in C++?

std::make_unique doesn't "prevent" memory-leak in the sense that it's still possible to write memory leaks in programs that use std::make_unique.

What does std::make_unique is make it easier to write programs that don't have memory leaks.


A or B gets leaked if an exception is thrown there.

Why?

Pre C++17:

Because if you allocate A, then call constructor of B before constructing the std::unique_ptr that was supposed to own A, and the constructor of B throws, then A will leak (or same happens with A and B reversed).

Since C++17:

There's no leak since the scenario described above cannot happen anymore in the shown example.

What does make_unique do that the default unique_ptr does not?

std::make_unique allocates memory, and either successfully returns a valid std::unique_ptr, or throws an exception without leaking memory.

std::unique_ptr(T*) accepts a pointer that was allocated separately. If an exception is thrown before the constructor is called, then there will never have been a unique pointer owning the allocation.


It's possible to fix the (pre-C++17) bug without using std::make_unique:

auto a = std::unique_ptr<A>{new A{}};
auto b = std::unique_ptr<B>{new B{}};
fn(std::move(a), std::move(b));

But if you always use std::make_unique, then you won't accidentally make the mistake of writing leaky version. Furthermore, std::make_unique lets you avoid writing new which allows you to use the rule of thumb "write exactly one delete for each new". 0 new -> 0 delete.

eerorika
  • 232,697
  • 12
  • 197
  • 326