133

What are the advantages of using std::make_unique over the new operator for initializing a std::unique_ptr?

In other words, why is

std::unique_ptr<SomeObject> a = std::make_unique(SomeObject(...))

better than doing

std::unique_ptr<SomeObject> a = new SomeObject(...)

I tried looking up a lot online and I do know that it is a good rule of thumb to avoid the operator new in modern C++, but I am not sure what the advantages are in this exact scenario. Does it prevent any kind of memory leaks that might happen? Is it faster to do a std::make_unique than to use new?

Cris Luengo
  • 43,236
  • 6
  • 46
  • 92
niting
  • 1,854
  • 3
  • 17
  • 20
  • 2
    Actually `std::make_unique(SomeObject(...))` results in calling constructor and copy/move constructor. Definitely, a better choice is to call `std::make_unique` directly with construtor parameters of underlying templated type. – stuhlo Jan 15 '19 at 22:17

2 Answers2

133

Advantages

  • make_unique teaches users "never say new/delete and new[]/delete[]" without disclaimers.

  • make_unique shares two advantages with make_shared (excluding the third advantage, increased efficiency). First, unique_ptr<LongTypeName> up(new LongTypeName(args)) must mention LongTypeName twice, while auto up = make_unique<LongTypeName>(args) mentions it once.

  • make_unique prevents the unspecified-evaluation-order leak triggered by expressions like foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)). (Following the advice "never say new" is simpler than "never say new, unless you immediately give it to a named unique_ptr".)

  • make_unique is carefully implemented for exception safety and is recommended over directly calling unique_ptr constructors.

When not to use make_unique

  • Don't use make_unique if you need a custom deleter or are adopting a raw pointer from elsewhere.

Sources

  1. Proposal of std::make_unique.
  2. Herb Sutter's GotW #89 Solution: Smart Pointers
Gabriel Staples
  • 11,777
  • 3
  • 74
  • 108
101010
  • 39,010
  • 10
  • 84
  • 149
  • 1
    Thank you for the answer. Could you explain the reasoning for your point on when not to use it, please? – DBedrenko Oct 31 '17 at 18:14
  • 1
    There's one additional case when not to use make_unique: In case of private or protected constructors! Please absent from listening to any suggestions on SO or the net recommending to make make_unique or unique_ptr or whatever a friend of your class. This is non-portable (as then you depend on the implementation details of unique_ptr and stuff). And it breaks the privateness of the constructor (that should have a reason to be private) as afterwards anybody could make_unique! – Don Pedro Jul 08 '19 at 10:29
  • I'm curious the same as this comment on Herb Sutter's writeup on this same topic https://herbsutter.com/gotw/_102/#comment-5725. Essentially what if such `make_unique` is inlined (let's assume it is), then the equivalent call to function which accepts multiple arguments won't have exception safety anymore? – haxpor Sep 05 '19 at 10:59
  • @haxpor The exception safety bases on the lifetime of objects which has nothing to do with optimizations like inlining. Your so called "equivalent call to function" is not equivalent since it does not take the scope of the function body of `make_unique<...>` into account. – Tobias Aug 12 '20 at 11:58
  • Regarding the 2nd point. Unfortunately make_unique does not seem to have robust support for initializer lists. So `unique_ptr(new TypeWithDeepMap({}))` turns to `make_unque(std::map>({}))` – Ark-kun Nov 17 '20 at 01:28
  • The _unspecified-evaluation-order leak_ problem [has been resolved](https://stackoverflow.com/questions/53870522/why-use-stdmake-unique-in-c17) as it is specified as of C++17. – Константин Ван Jan 09 '21 at 04:29
64

The difference is that std::make_unique returns an object of type std::unique_ptr and new returns a pointer to the created object. For memory allocation failures, they will both throw. Hold on, it's not that simple. Read further.

Consider such a function below:

void func(ClassA* a, ClassB* b){
     ......
}

When you make a call like func(new A(), new B()); The compiler may choose to evaluate the function arguments from left to right, or in any order it so wishes. Let's assume left to right evaluation: What happens when the first new expression succeeds but the second new expression throws?

The real danger here is when you catch such exception; Yes, you may have caught the exception thrown by new B(), and resume normal execution, but new A() already succeeded, and its memory will be silently leaked. Nobody to clean it up... * sobs...

But with make_unique, you cannot have a leak because, stack unwinding will happen ( and the destructor of the previously created object will run). Hence, having a preference for make_unique will constrain you towards exception safety. In this case, std::make_unique provides a "Basic Exception Safety" that the memory allocated and object created by new will never be orphaned no matter what. Even till the ends of time... :-)

You should read Herb Sutter GoTW102

WhiZTiM
  • 19,970
  • 3
  • 36
  • 56
  • 3
    @Nik-Lz `func(new A(), new B())` doesn't make a `unique_ptr`, the answer was about what happens when you don't use it... – Purple Ice Nov 26 '18 at 17:40