C++ Unique ownership (std::unique_ptr)



A std::unique_ptr is a class template that manages the lifetime of a dynamically stored object. Unlike for std::shared_ptr, the dynamic object is owned by only one instance of a std::unique_ptr at any time,

// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);

(Note: std::unique_ptr is available since C++11 and std::make_unique since C++14.)

Only the variable ptr holds a pointer to a dynamically allocated int. When a unique pointer that owns an object goes out of scope, the owned object is deleted, i.e. its destructor is called if the object is of class type, and the memory for that object is released.

To use std::unique_ptr and std::make_unique with array-types, use their array specializations:

// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);

// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);

You can access the std::unique_ptr just like a raw pointer, because it overloads those operators.

You can transfer ownership of the contents of a smart pointer to another pointer by using std::move, which will cause the original smart pointer to point to nullptr.

// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();

// Change value to 1
*ptr = 1;

// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);

int a = *ptr2; // 'a' is 1
int b = *ptr;  // undefined behavior! 'ptr' is 'nullptr'
               // (because of the move command above)

Passing unique_ptr to functions as parameter:

void foo(std::unique_ptr<int> ptr)
    // Your code goes here

std::unique_ptr<int> ptr = std::make_unique<int>(59);

Returning unique_ptr from functions. This is the preferred C++11 way of writing factory functions, as it clearly conveys the ownership semantics of the return: the caller owns the resulting unique_ptr and is responsible for it.

std::unique_ptr<int> foo()
    std::unique_ptr<int> ptr = std::make_unique<int>(59);
    return ptr;

std::unique_ptr<int> ptr = foo();

Compare this to:

int* foo_cpp03();

int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
                      // it's not readily apparent what the answer is.

The class template make_unique is provided since C++14. It's easy to add it manually to C++11 code:

template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }

Unlike the dumb smart pointer (std::auto_ptr), unique_ptr can also be instantiated with vector allocation (not std::vector). Earlier examples were for scalar allocations. For example to have a dynamically allocated integer array for 10 elements, you would specify int[] as the template type (and not just int):

std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);

Which can be simplified with:

auto arr_ptr = std::make_unique<int[]>(10);

Now, you use arr_ptr as if it is an array:

arr_ptr[2] =  10; // Modify third element

You need not to worry about de-allocation. This template specialized version calls constructors and destructors appropriately. Using vectored version of unique_ptr or a vector itself - is a personal choice.

In versions prior to C++11, std::auto_ptr was available. Unlike unique_ptr it is allowed to copy auto_ptrs, upon which the source ptr will lose the ownership of the contained pointer and the target receives it.