Unique Pointer (unique_ptr
)
Introduced since C++11, included in the <memory>
library. Learned formally in CS247.
Use cases for
unique_ptr
unique_ptr
are good for representing ownership (owns-a), since when one object dies, itsunique_ptr
fields will run and clean up the associated object.
If you want a has-a relationship, use raw pointers or references instead.
Usage
Links
You can access the underlying raw pointer of a smart pointer via .get()
.
Motivation
In CS247, the teacher motivated the use of smart pointers after introducing Exceptions. In particular, consider the following function:
- Under normal circumstances,
f
does not leak memory. But, ifg
throws, then we do not executedelete p
, so we do leak memory!
if we were to fix f
with a try catch
block, we could have something like this:
One solution: wrap the pointer in a stack allocated object that will delete it for us during stack unwinding, which is exactly what a unique_ptr
does!
Applying RAII to dynamic memory gives us std::unique_ptr<T>
(in <memory>
library)
- contains a
T*
passed via the constructor - deletes the
T*
in the destructor
If we leave f
normally, or if we leave f
during stack unwinding, either way, p
’s destructor runs, deletes heap memory (because of implement of unique_ptr
).
In between, we can use p
like a regular pointer thanks to operator overload.
CAREFUL
Do NOT create
unique_ptr
with a pointer
Generally, we do not call the constructor directly with a pointer, because of several issues (more below).
The issues:
std::unique_ptr<T> p{new T{}};
new is not paired with a delete (not one that we see, at least)- Causes a double delete:
g()
can potentially throw in the example below, heap-allocated object does not get deleted
One potential ordering (in C++14) (obscure scenario)
new T()
g()
unique_ptr
constructorf()
Preferred alternative:
std::make_unique<T>
(...)This constructs a
T
object in the heap with arguments (…), and returns aunique_ptr<T>
pointing at this object.
I saw something like the PImpl Idiom, in that case they are using the tructor to construct the new unique pointer.
BE CAREFUL with Syntax
Misremembering the syntax, it can be easy to make a mistake like this:
- Notice the
*
. This actually is a unique pointer to a pointer. is this valid syntax? How does it get constructed?make_unique<Sword*>()
will allocate memory for a raw pointer toSword
on the heap.
I think the confusion came from learning about static_cast, in which case we do include the *
in the <>
template arguments.
More Food for Thought
Something else to consider:
Call copy constructor to copy p
into q
.
What happens? Doesn’t compile. Copying is disabled for unique_ptr
s (achieved with = delete keyword), they can only be moved.
unique_ptr vs. object fields for owns-a relationship
Which one should you use, since both represent owns-a relationship?
Object Fields do not enable you to have Polymorphism. If you use object references, then it isn’t a owns-a relationship anymore. On the other hand, with unique_ptr
, you are NOT allowed to copy construct.
A unique_ptr
can only be moved.
Pass by Value vs. Pass by Reference for Unique Pointers
What is the different between
unique_ptr<Level>
andunique_ptr<level>&
?Ran into this while working on my Biquadris project.
unique_ptr<Level>
owns and manages the pointer to an object of typeLevel
. When theunique_ptr
goes out of scope, the object it points to is automatically deleted.unique_ptr<Level>&
is a reference to aunique_ptr
that manages aLevel
object. It does not own the object; it simply refers to an existingunique_ptr
.So it’s about ownership. Remember that Object Destruction happens when the object gets out of scope. This does NOT happen for References.
So in general, you probably always want to pass the pointer by reference, UNLESS you are trying to transfer ownership.
Passing by Value (Ownership Transfer):
Passing by Reference (Access without Ownership Transfer):
Under the Hood, how is unique_ptr
implemented?
This is code provided by Ross Evans, professor of CS247. You should try to reason and come up with your own implementation.