unique_ptr
and Dynamic Memory AllocationA common programming practice is to allocate dynamic memory, assign the address of that memory to a pointer, use the pointer to manipulate the memory and deallocate the memory with delete
(or delete[]
) when the memory is no longer needed. If an exception occurs after successful memory allocation but before the delete
statement executes, a memory leak could occur. C++11 provides class template unique_ptr
in header <memory>
to deal with this situation. A unique_ptr
maintains a pointer to dynamically allocated memory. When a unique_ptr
object goes out of scope, its destructor is called, which performs a delete
(or delete[]
) operation on the unique_ptr
object’s pointer data member. Class template unique_ptr
provides overloaded operators *
and ->
so that a unique_ptr
object can be used just like a regular pointer variable.
unique_ptr
Figure 17.9 demonstrates a unique_ptr
object that points to a dynamically allocated object of our custom class Integer
(Figs. 17.7–17.8).
Line 14 of Fig. 17.9 creates unique_ptr
object ptrToInteger
and initializes it with a pointer to a dynamically allocated Integer
object that contains the value 7
. To initialize the unique_ptr
, line 14 uses C++14’s make_unique
function template, which allocates dynamic memory with operator new
, then returns a unique_ptr
to that memory. Prior to C++14, you’d pass the result of a new
expression directly to unique_ptr
’s constructor.
Line 17 uses the unique_ptr
overloaded ->
operator to invoke function setInteger
on the Integer
object that ptrToInteger
manages. Line 20 uses the unique_ptr
overloaded *
operator to dereference ptrToInteger
, then uses the dot (.
) operator to invoke function getInteger
on the Integer
object. Like a regular pointer, a unique_ptr
’s ->
and *
overloaded operators can be used to access the object to which the unique_ptr
points.
Because ptrToInteger
is a non-static
local variable in main
, it’s destroyed when main
terminates. The unique_ptr
destructor delete
s the dynamically allocated Integer
object, which calls the Integer
object’s destructor. The memory that Integer
occupies is released, regardless of how control leaves the block (e.g., by a return
statement or by an exception). Most importantly, using this technique can prevent memory leaks. For example, suppose a function returns a pointer aimed at some object. Unfortunately, the function caller that receives this pointer might not delete
the object, thus resulting in a memory leak. However, if the function returns a unique_ptr
to the object, the object will be deleted automatically when the unique_ptr
object’s destructor gets called.
unique_ptr
OwnershipThe class is called unique_ptr
because only one unique_ptr
at a time can own a dynamically allocated object. When you assign one unique_ptr
to another, the unique_ptr
on the assignment’s right transfers ownership of the dynamic memory it manages to the unique_ptr
on the assignment’s left. The same is true when one unique_ptr
is passed as an argument to another unique_ptr
’s constructor. (These operations use unique_ptr
’s move assignment operator and move constructor—we discuss move semantics in Chapter 24.) The last unique_ptr
object that maintains the pointer to the dynamic memory will delete the memory. This makes unique_ptr
an ideal mechanism for returning dynamically allocated memory to client code. When the unique_ptr
goes out of scope in the client code, the unique_ptr
’s destructor deletes the dynamically allocated object—if the object has a destructor, it is called before the memory is returned to the system.
unique_ptr
to a Built-In ArrayYou can also use a unique_ptr
to manage a dynamically allocated built-in array. For example, consider the statement
unique_ptr<string[]> ptr{make_unique<string[]>(10)};
Because make_unique
’s type is specified as string[]
, the function obtains a dynamically allocated built-in array of the number of elements specified by its argument (10). By default, the elements of arrays allocated with make_unique
are initialized to 0 for fundamental types, to false
for bool
s or via the default constructor for objects of a class—so in this case, the array would contain 10 string
objects initialized with the empty string
.
A unique_ptr
that manages an array provides an overloaded []
operator for accessing the array’s elements. For example, the statement
ptr[2] = "hello";
assigns "hello"
to the string
at ptr[2]
and the following statement displays that string
cout << ptr[2] << endl;