Intermediate · Templates, smart pointers and move semantics · topic 27 of 42 · 30 min
Smart pointers 1: std::unique_ptr
Explanation
Why smart pointers?
Earlier you saw that raw new and delete cause leaks, double frees and use-after-free bugs. A smart pointer is a class that holds a pointer and applies RAII to it: when the smart pointer goes away, it deletes the object. The standard library gives two main ones in <memory>: std::unique_ptr (single owner) and std::shared_ptr (shared owners).
#include <iostream>
#include <memory>
#include <string>
struct Report {
std::string title;
explicit Report(std::string t) : title(std::move(t)) { std::cout << "made " << title << '\n'; }
~Report() { std::cout << "freed " << title << '\n'; }
};
int main() {
auto r = std::make_unique<Report>("Sales Q1"); // std::unique_ptr<Report>
std::cout << r->title << '\n'; // use like a pointer
if (true) return 0; // early return: still freed
}
// made Sales Q1
// Sales Q1
// freed Sales Q1One owner: move, never copy
auto a = std::make_unique<int>(5);
// auto b = a; // error: unique_ptr cannot be copied
auto b = std::move(a); // ownership moves to b; a is now empty (nullptr)
if (!a) std::cout << "a is empty\n";
std::cout << *b << '\n'; // 5Passing unique_ptr to functions
| Function wants to | Parameter type | Call |
|---|---|---|
| just use the object | const T& or T* | use(*ptr) or use(ptr.get()) |
| take ownership | std::unique_ptr<T> (by value) | take(std::move(ptr)) |
| maybe replace what the caller owns | std::unique_ptr<T>& | reset(ptr) (rare) |
Most functions only need to use an object, not own it. They should take a reference (or a raw pointer if "nothing" is allowed). A raw pointer that does not own anything is fine in modern C++; what you avoid is a raw pointer that must be deleted.
Useful members
- ptr.get() returns the raw pointer without giving up ownership.
- ptr.reset() deletes the object now and makes ptr empty; ptr.reset(new T) replaces it.
- ptr.release() gives up ownership and returns the raw pointer; you must then delete it yourself (rarely needed).
- if (ptr) tests whether it holds an object.
- std::make_unique<T[]>(n) creates an array, but std::vector is usually better.
unique_ptr in containers and classes
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(2.0));
shapes.push_back(std::make_unique<Square>(3.0));
// the vector owns the shapes; they are deleted when the vector is destroyed
class Game {
std::unique_ptr<Renderer> renderer_; // Game owns its renderer; rule of zero still holds
};Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Smart pointers 1: std::unique_ptr · 1/6
Smart pointers apply RAII to heap memory, so you never write delete again.
Prefer a video by a teacher? Find free videos on “c++ unique_ptr make_unique tutorial ownership move” on YouTube ↗
In real life: Loading a level in a mobile game
A game creates a Level object on the heap when you enter a stage and frees it when you leave. Holding it in a std::unique_ptr inside the Game class means loading the next level with level_ = std::make_unique<Level>(2) automatically frees the old level first. No leaks, even after hundreds of levels in one session.
#include <iostream>
#include <memory>
struct Level {
int number;
explicit Level(int n) : number(n) { std::cout << "load " << n << '\n'; }
~Level() { std::cout << "unload " << number << '\n'; }
};
class Game {
public:
void enter(int n) { level_ = std::make_unique<Level>(n); } // old level freed here
private:
std::unique_ptr<Level> level_;
};
int main() {
Game g;
g.enter(1);
g.enter(2);
}
// load 1
// load 2
// unload 1
// unload 2Practice (0/4 done)
Which line creates a unique_ptr to an int holding 10?
What prints? auto a = std::make_unique<int>(7); auto b = std::move(a); std::cout << (a ? "a" : "-") << (b ? "b" : "-");
Which line does NOT compile?
Rewrite this leaky code with std::unique_ptr so that nothing leaks on any path, and no delete appears: void process(bool fail) { Buffer* b = new Buffer(1024); if (fail) return; use(*b); delete b; }
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Smart pointers 2: std::shared_ptr and std::weak_ptrTemplates, smart pointers and move semantics · same module
- Move semantics: steal instead of copyTemplates, smart pointers and move semantics · same module
- Pointers, the stack and the heapReferences, pointers, arrays and strings
- Box<T>: values on the heapRust Programming · Closures, iterators and smart pointers
- C++ interview questions and how to answer themCompetitive programming, interviews and careers
- Pointers to structs and the arrow operatorC Programming (from zero to systems) · Structs, unions, enums and files
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
When is the object owned by a unique_ptr deleted?
Answer: When the unique_ptr is destroyed or reset
The destructor of unique_ptr deletes the object.
How do you pass ownership of a unique_ptr into a function sink(std::unique_ptr<T> p)?
Answer: sink(std::move(p))
Ownership must be moved explicitly.
What does p.get() return?
Answer: The raw pointer, while p keeps ownership
get() is for passing to code that only uses the object.
What is the size overhead of std::unique_ptr<T> with the default deleter?
Answer: Usually none: same size as a raw pointer
It is a zero-overhead wrapper in practice.
Why prefer std::make_shared over std::shared_ptr<T>(new T)?Related
Answer: One allocation for object and control block, and no raw new in your code
make_shared is faster and safer.
Which pair makes a deep copy automatically when you copy the object?Related
Answer: A std::vector member
std::vector copies all its elements into new memory.
Why should you avoid creating an RAII object with new?Related
Answer: Its destructor only runs when you delete it, so the automatic cleanup is lost
RAII relies on automatic lifetime. A heap object lives until someone deletes it.
Which memory is cleaned up automatically when a function returns?Related
Answer: The function's local variables on the stack
Stack variables are destroyed at the end of their scope; heap memory stays until deleted.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Templates, smart pointers and move semantics” before its exam: Smart pointers 2: std::shared_ptr and std::weak_ptr, Move semantics: steal instead of copy.
- Module 7 exam: Templates, smart pointers and move semantics · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Modern C++: lambdas, C++17 and C++20 essentials. 42 topics left in the course before the final exam.