28

An MSDN page about smart pointers includes a promoted warning about creating smart pointers in parameter lists:

Always create smart pointers on a separate line of code, never in a parameter list, so that a subtle resource leak won’t occur due to certain parameter list allocation rules.

What are the parameter list allocation rules that it is referring to? Under what circumstances can the resource leak occur?

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
fo_
  • 872
  • 8
  • 8

3 Answers3

21

It's referring to the possibility of evaluating parameters in a different order, e.g.

func(unique_ptr<MyClass>(new MyClass), a(), b());

if the order of evaluation is: a(), MyClass(), b(), then unique_ptr is constructed, it might happen that b() throws and memory will be leaked.

A safeguard (that has been added in C++14 and it's also more efficient) is to use make_unique (assuming MSVC and according to your compiler version, you might have to define one yourself or take a look here). The same applies to shared_ptr.

Take a look at the notes for std::make_shared here, too:

Moreover, code such as f(std::shared_ptr<int>(new int(42)), g()) can cause a memory leak if g throws an exception because g() may be called after new int(42) and before the constructor of shared_ptr<int>. This doesn't occur in f(std::make_shared<int>(42), g()), since two function calls are never interleaved.

Community
  • 1
  • 1
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 1
    Since the OP appears to be using VS, I think `make_unique` is already supported in VS2013. – dyp Sep 24 '14 at 14:25
  • Thanks, edited for clarity. VS2012 might be a problem since it doesn't support variadic templates, I provided a fallback link with the issue. (or OP might try CTP, I believe update 2 should work) – Marco A. Sep 24 '14 at 14:28
  • 1
    Anyway, there are additional reasons to prefer `allocate_shared` and `make_shared` to doing that manually: Efficiency. – Deduplicator Sep 24 '14 at 14:31
  • @Deduplicator sure, I didn't write "*that's the reason you should use X*" because it also provides other benefits (and also readability in my opinion, but that might be argued) – Marco A. Sep 24 '14 at 14:33
  • 1
    A minor nit-pick: I would be extremely astounded if `make_unique` was any more or less efficient than calling `std::unique_ptr`s ctor yourself directly. – Deduplicator Sep 24 '14 at 14:40
  • I did have some trouble understanding the answer but are you saying the code may execute the inner bracket of shared_ptr constructor and then execute g() before it has executed the shared_ptr constructor? This seems quite an odd sequence for the compiler to choose. Why would the standard allow it? Does make_shared get around this issue? – Robinson Sep 16 '15 at 16:55
  • 1
    @Robinson Take a look at 5.2.2.8 and integrate it with [herb's Q&A](http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/) – Marco A. Sep 16 '15 at 18:05
  • `make_unique` has nothing to do with efficiency, it's entirely to do with reducing redundancy (no specifying the type twice) and to enable better sequencing when used in a parameter list. `make_shared`, on the other hand, does prevent a duplicate allocation -- but this is also available in C++11. It might be worth clarifying, since the current wording sounds as though `make_shared` is C++14 as well -- which is incorrect – Human-Compiler Nov 10 '20 at 16:03
5

If you do this:

func(shared_ptr<Foo>(new Foo), shared_ptr<Bar>(new Bar));

And the signature is:

void func(shared_ptr<Foo>, shared_ptr<Bar>);

What will happen if one of the constructors throws? It may happen that new has been called once successfully and then the other one fails (you don't know which one will get called first). If that happens, one of the objects could be leaked, because it was never held by a resource manager.

You can read more here: http://www.gotw.ca/gotw/056.htm

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
2

The problem is that if you have function that takes several arguments:

void func( const std::shared_ptr< MyFirstClass >& ptr, const MySecondClass& ref );

and you call this function like this:

func( std::shared_ptr< MyFirstClass >( new MyFirstClass ), MySecondClass() );

the compiler is free to emit code that executes the expressions in the argument list in any order it likes. Some of these orders can be problematic. For instance, imagine that the compiler decides to emit code that first executes

new MyFirstClass

and then

MySecondClass()

and finally the c'tor of std::shared_ptr< MyFirstClass > (passing it the address of the instance of MyFirstClass that was allocated on the free store in the first step).

So far so good. But if the second step throws an exception, then the shared_ptr never gets constructed, and your free store allocated MyFirstClass-instance is forever lost.

antred
  • 3,697
  • 2
  • 29
  • 37