Search

15.5 — std::unique_ptr

At the beginning of the chapter, we discussed how use of pointers can lead to bugs and memory leaks in some situations. For example, this can happen when a function early returns, or throws an exception, and the pointer is not properly deleted.

Now that we’ve covered the fundamentals of move semantics, we can return to the topic of smart pointer classes. As a reminder, a smart pointer is a class that manages a dynamically allocated object. Although smart pointers can offer other features, the defining characteristic of a smart pointer is that it manages a dynamically allocated resource, and ensures the dynamically allocated object is properly cleaned up at the appropriate time (usually when the smart pointer goes out of scope).

Because of this, smart pointers should never be dynamically allocated themselves (otherwise, there is the risk that the smart pointer may not be properly deallocated, which means the object it owns would not be deallocated, causing a memory leak). By always allocating smart pointers statically (as local variables or composition members of a class), we’re guaranteed that the smart pointer will properly go out of scope when the function or object it is contained within ends, ensuring the object the smart pointer owns is properly deallocated.

C++11 standard library ships with 4 smart pointer classes: std::auto_ptr (which you shouldn’t use -- it’s being removed in C++17), std::unique_ptr, std::shared_ptr, and std::weak_ptr. std::unique_ptr is by far the most used smart pointer class, so we’ll cover that one first. In the next lesson, we’ll cover std::shared_ptr and std::weak_ptr.

std::unique_ptr

std::unique_ptr is the C++11 replacement for std::auto_ptr. It should be used to manage any dynamically allocated object that is not shared by multiple objects. That is, std::unique_ptr should completely own the object it manages, not share that ownership with other classes. std::unique_ptr lives in the <memory> header.

Let’s take a look at a simple smart pointer example:

Because the std::unique_ptr is allocated on the stack here, it’s guaranteed to eventually go out of scope, and when it does, it will delete the Resource it is managing.

Unlike std::auto_ptr, std::unique_ptr properly implements move semantics.

This prints:

Resource acquired
res1 is not null
res2 is null
Ownership transferred
res1 is null
res2 is not null
Resource destroyed

Because std::unique_ptr is designed with move semantics in mind, copy initialization and copy assignment are disabled. If you want to transfer the contents managed by std::unique_ptr, you must use move semantics. In the program above, we accomplish this via std::move (which converts res1 into an r-value, which triggers a move assignment instead of a copy assignment).

Accessing the managed object

std::unique_ptr has an overloaded operator* and operator-> that can be used to return the resource being managed. Operator* returns a reference to the managed resource, and operator-> returns a pointer.

Remember that std::unique_ptr may not always be managing an object -- either because it was created empty (using the default constructor or passing in a nullptr as the parameter), or because the resource it was managing got moved to another std::unique_ptr. So before we use either of these operators, we should check whether the std::unique_ptr actually has a resource. Fortunately, this is easy: std::unique_ptr has a cast to bool that returns true if the std::unique_ptr is managing a resource.

Here’s an example of this:

This prints:

Resource acquired
I am a resource
Resource destroyed

In the above program, we use the overloaded operator* to get the Resource object owned by std::unique_ptr res, which we then send to std::cout for printing.

std::unique_ptr and arrays

Unlike std::auto_ptr, std::unique_ptr is smart enough to know whether to use scalar delete or array delete, so std::unique_ptr is okay to use with both scalar objects and arrays.

However, std::array or std::vector (or std::string) are almost always better choices than using std::unique_ptr with a fixed array, dynamic array, or C-style string.

Rule: Favor std::array, std::vector, or std::string over a smart pointer managing a fixed array, dynamic array, or C-style string

std::make_unique

C++14 comes with an additional function named std::make_unique(). This templated function constructs an object of the template type and initializes it with the arguments passed into the function.

The code above prints:

3/5
0/1

Use of std::make_unique() is optional, but is recommended over creating std::unique_ptr yourself. This is because code using std::make_unique is simpler, and it also requires less typing (when used with automatic type deduction). Furthermore it resolves an exception safety issue that can result from C++ leaving the order of evaluation for function arguments unspecified.

Rule: use std::make_unique() instead of creating std::unique_ptr and using new yourself

The exception safety issue in more detail

For those wondering what the “exception safety issue” mentioned above is, here’s a description of the issue.

Consider an expression like this one:

The compiler is given a lot of flexibility in terms of how it handles this call. It could create a new T, then call function_that_can_throw_exception(), then create the std::unique_ptr that manages the dynamically allocated T. If function_that_can_throw_exception() throws an exception, then the T that was allocated will not be deallocated, because the smart pointer to do the deallocation hasn’t been created yet. This leads to T being leaked.

std::make_unique() doesn’t suffer from this problem because the creation of the object T and the creation of the std::unique_ptr happen inside the std::make_unique() function, where there’s no ambiguity about order of execution.

Returning std::unique_ptr from a function

std::unique_ptr can be safely returned from a function by value:

In the above code, createResource() returns a std::unique_ptr by value. If this value is not assigned to anything, the temporary return value will go out of scope and the Resource will be cleaned up. If it is assigned (as shown in main()), move semantics will be employed to transfer the Resource from the return value to the object assigned to (in the above example, ptr). This makes returning a resource by std::unique_ptr much safer than returning raw pointers!

In general, you should not return std::unique_ptr by pointer (ever) or reference (unless you have a specific compelling reason to).

Passing std::unique_ptr to a function

If you want the function to take ownership of the contents of the pointer, pass the std::unique_ptr by value. Note that because copy semantics have been disabled, you’ll need to use std::move to actually pass the variable in.

The above program prints:

Resource acquired
I am a resource
Resource destroyed
Ending program

Note that in this case, ownership of the Resource was transferred to takeOwnership(), so the Resource was destroyed at the end of takeOwnership() rather than the end of main().

However, most of the time, you won’t want the function to take ownership of the resource. Although you can pass a std::unique_ptr by reference (which will allow the function to use the object without assuming ownership), you should only do so when the caller might alter or change the object being managed.

Instead, it’s better to just pass the resource itself (by pointer or reference, depending on whether null is a valid argument). This allows the function to remain agnostic of how the caller is managing its resources. To get a raw resource pointer from a std::unique_ptr, you can use the get() member function:

The above program prints:

Resource acquired
I am a resource
Ending program
Resource destroyed

std::unique_ptr and classes

You can, of course, use std::unique_ptr as a composition member of your class. This way, you don’t have to worry about ensuring your class destructor deletes the dynamic memory, as the std::unique_ptr will be automatically destroyed when the class object is destroyed. However, do note that if your class object is dynamically allocated, the object itself is at risk for not being properly deallocated, in which case even a smart pointer won’t help.

Misusing std::unique_ptr

There are two easy ways to misuse std::unique_ptrs, both of which are easily avoided. First, don’t let multiple classes manage the same resource. For example:

While this is legal syntactically, the end result will be that both res1 and res2 will try to delete the Resource, which will lead to undefined behavior.

Second, don’t manually delete the resource out from underneath the std::unique_ptr.

If you do, the std::unique_ptr will try to delete an already deleted resource, again leading to undefined behavior.

Note that std::make_unique() prevents both of the above cases from happening inadvertently.

Quiz time

1) If your class has a smart pointer member, why should you try to avoid allocating objects of that class dynamically?

Show Solution

2) Convert the following program from using a normal pointer to using std::unique_ptr where appropriate:

Show Solution

15.6 -- std::shared_ptr
Index
15.4 -- std::move

40 comments to 15.5 — std::unique_ptr

  • DecSco

    I think the solution of question 2 makes sense, but the question leaves it very open, whether or not to convert the print function as well.

    • Alex

      I added "where appropriate", to help hint at the fact that it might not be appropriate to convert all pointers in this case. Thanks for the feedback!

  • lovel

    Hey, Alex. I got a question here.

    In the first 2 statements, res is a l-value there, but ptr1 can use this l-value in constructor. However, in the third statement, ptr1 is a l-value, but it cannot be passed to the constructor. What I think is that we cannot pass a l-value to a std::unique_ptr constructor, but this situation makes me really confused.
    BTW, I really appreaciate your tutorial and learned a lot here. I come back to this site now and then to learn new things and review what I'v learned. Thanks, Alex.

    • nascardriver

      Hi lovel!

      First, @ptr1 is not a C-style pointer, so you cannot construct an @std::unique_ptr from it.
      Second, if @std::unique_ptr had a constructor that accepts an @std::unique_ptr you'd end up with two @std::unique_ptrs managing the same resource, which, will cause you nothing but trouble.

  • David

    I have a question regarding move semantics.

    vs.

    Would the use of std::move() be redundant and inefficient? Should I be using std::move() in this case, or should I be inserting the std::unique_ptr<> directly to the vector? Thanks again!

  • Topherno

    Wait, so the takeaway from the "Misusing std::unique_ptr" section is that unique_ptr has the same "critical flaw" that Auto_ptr1 has in 15.1, since multiple unique_ptr's can manage the same resource? (And thus we can get dangling pointers if one of the smart pointers goes out of scope?)

    If I understand correctly, make_unique() solves this by always returning a new dyn alloc'd object at a different memory location, so you'll never accidentally assign an object to a unique_ptr that's already being managed by another unique_ptr. And thus you don't have to worry about any dangling pointers. It would seem then that make_unique is not just recommended, but absolutely vital!

    • Alex

      Close. Yes, unique_ptr has the same flaw as Auto_ptr1, in that if you end up with two pointers managing the same object, you're going to run into problems. make_unique() solves this in the way you suggest, and helps prevent errors.

      But if you want to manage creation of objects yourself (for whatever reason), use of make_unique() isn't strictly necessary. Neither is it useful if you want to make a smart pointer take over management of an already created dumb pointer.

      • Topherno

        Thanks for the answer! Interesting. Why doesn't unique_ptr have some kind of safeguard mechanism protecting against attempts to point more than one unique_ptr at the same resource? Seems like a pretty glaring flaw, although to be fair they must've had a good reason for not safeguarding against it. Taking a page out of shared_ptr's book, why not some kind of global control block which tracks which resources are being managed by a unique_ptr?

  • Ryan

    "Rule: Favor std:array, std::vector, or std::string over a smart pointer managing a fixed array, dynamic array, or C-style string"

    Question 1: Did you mean to say "std::array, rather than std:array?"

    Question 2: In the case of using array, vector, or string, how can these containers be used with dynamically allocated memory? Can they clean themselves up like smart pointers?

    • nascardriver

      Hi Ryan!

      1. "std:array" isn't valid syntax, probably a typo.

      2. If you create an @std::vector and it reaches the end of it's life (eg. end of scope) the @std::vector will free all the memory it allocated.
      If you dynamically create an @std::vector pointer you need to manually delete it. The @std::vector deletes the memory it allocated.
      If you create an @std::vector and use it to store pointers to dynamically allocated memory you need to delete that memory manually.
      If this doesn't answer your question try giving an example where you're unsure what happens.

    • Alex

      1) Yes, this was a typo. Fixed!
      2) std::vector and std::string allocate memory dynamically to hold their contents, and will clean up after themselves when they are destroyed (they don't need to use a smart pointer for this since the destructor can do the cleanup). std::array does not use dynamically allocated memory.

      You can allocate any of these dynamically yourself, but if you do, then you are responsible for the cleanup.

  • Advice

    Hi Alex!
    wanted to show u the link i read how to properly pass a unique ptr ( without taking ownership) http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r32-take-a-unique_ptrwidget-parameter-to-express-that-a-function-assumes-ownership-of-a-widget
    so it should be void printFraction(Fraction* ptr) and call the function with rintFraction(ptr.get());

  • Jujinko

    Hey Alex,

    Did you accidentally type an "example" too much?

    "Let’s take a look at a simple example smart pointer example:"

  • KnowMore

    Alex?

    Isn't the "ptr" set to null in this case(as ownership was transferred from ptr to ptr1) ?
    If yes, then why does de-referencing it not causes undefined behaviour and even prints the right(but undesired) output?
    Waiting for your reply....
    Thanks in Advance 🙂

    • KnowMore

      Ah ! That's such a foolish one ! It calls the default/value constructor in this case not the move constructor.
      Sorry for Disturbing Man !

    • Alex

      No, ptr is not set to nullptr. std::unique_ptr's constructor takes the argument by address, not by reference to an address, so it's unable to modify the argument passed in.

      • KnowMore

        Isn't it that, ptr is not getting set to null because it is not of type unique_ptr instead it is just an int ptr.
        So, it calls the value constructor of unique_ptr class not the move one.
        And, when I use another unique_ptr in the parenthesis ( with std::move ) of a unique_ptr it calls the move constructor and shows behaviour as expected (setting passed unique_ptr to null ).

        • Alex

          Yes, ptr is an lvalue so it's calling the normal constructor. They could have implemented it in such a way that the normal constructor set the argument to nullptr, but they didn't. Not sure why -- since unique_ptr is now managing the object, it actually kind of makes sense that it should.

          • KnowMore

            I think they just changed the copy semantics to move semantics in the case of copy cont. and assignment, rest everything is same 🙂

  • Martin

    I heard that one should pass the content of a unique pointer as a reference to a function, rather than the unique pointer itself as you showed in the example above. That way the function will work with unique pointers (by dereferencing them) and standard references to the object?
    Which one should be preferred?

    • Alex

      Yes, the article explicitly says you should pass a std::unique_ptr by (const) reference if you want to pass it to a function to be used. You should only pass it by value if you mean for the function to take ownership.

      • Martin

        Oh sorry there was a misunderstanding. I meant instead of writing code like this:

        void function(const std::unique_ptr<foo>& ref){...}

        One should write rather functions like this:

        void function (const foo& ref){...}

        and call them by dereferencing the unique pointer using the *-operator, e.g.

        auto ptr = std::make_unique<foo>();
        function (*ptr);

        Otherwise one would need special versions of each function for unique and shared pointer as well as references.

  • Hi, just one minor comment. In chapter 15.1 you said "std::auto_ptr has been deprecated in C++11, and it should not be used. In fact, std::auto_ptr is slated for complete removal from the standard library as part of C++17!" but here you say "std::auto_ptr (which you shouldn’t use -- it’s being deprecated in C++17)". 15.1 statement is the correct one if I'm not wrong.

  • john

    "This is because code using std::make_unique is simpler, requires less typing (when used with automatic type deduction), and it resolves an exception safety issue that can result from C++ leaving the order of evaluation for function arguments unspecified."

    I don't understand this part. Can you explain this in more detail?

    • Alex

      I've added a new section to the lesson explaining this in more detail. Have a read and let me know if you still have any questions about this.

  • john

    First answer: "Smart pointer members will only allocate their resource..." - shouldn't this be deallocate?

    There's another mistake in the code of question 2: privatea instead of private:

  • Diana

    In the "Accessing the managed object" example

    uses std::cout instead of the out parameter.

  • Bobix Louis

    Hi Alex,
      I was wondering why it is not allowed to pass a unique_ptr to a function by value(without using std::move) even though it allows to return a unique_ptr by value from a function.

    Since the copy constructors are not implemented for unique_ptr, ideally it should not allow somebody to return a unique_ptr by value from a function right?

    • Alex

      Great question. Function parameters are l-values (because they have an identifier). If you were to pass such a function an l-value std::unique_ptr, you'd be invoking copy semantics, which is disallowed by std::unique_ptr. You _can_ pass a unique_ptr to a function by value without using std::move as long as it's an r-value unique_ptr. If your unique_ptr is an l-value, you have to convert it to an r-value using std::move to ensure move semantics are invoked.

      However, return values are treated as r-values, because they have no name. So when we return a unique_ptr from a function, there's an implicit conversion to r-value that happens as part of the return process.

      • Bobix Louis

        Thanks Alex for the clear explanation, now i got it.

      • Topherno

        In the example in this section where we return a unique_ptr by value:

        it seems totally intuitive that move semantics are invoked since std::make_unique<Resource>() is a r-value (a temporary).

        What if, instead of an r-value, we return an l-value by value, like we did in the Auto_ptr4 example of 15.3 with "res"...It will still be moved right? I think this is what you were saying in the section "Automatic l-values returned by value may be moved instead of copied" in 15.3.

        • Alex

          Most likely, but it's up to the compiler to determine whether this can be done, so it's not something that should necessarily be relied upon.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]