Lesson 14 • Expert
Move Semantics & Smart Pointers
Eliminate manual memory management with unique_ptr, shared_ptr, weak_ptr, and understand move semantics for zero-cost resource transfers.
What You'll Learn
- ✓ unique_ptr for exclusive ownership
- ✓ shared_ptr and weak_ptr for shared ownership
- ✓ Move constructors and move assignment
- ✓ std::move and rvalue references
Why Smart Pointers?
In the previous lesson, you saw how raw new/delete can cause leaks and crashes. Smart pointers wrap raw pointers and automatically call delete when they go out of scope. Think of them as pointers with a built-in "self-destruct" — no more forgetting to free memory.
| Type | Ownership | Use Case |
|---|---|---|
unique_ptr | Single owner | Default choice — 95% of cases |
shared_ptr | Multiple owners | Shared resources, caches |
weak_ptr | Non-owning observer | Breaking circular references |
unique_ptr — Exclusive Ownership
Create, transfer, and auto-delete resources with unique_ptr
#include <iostream>
#include <memory>
using namespace std;
class Player {
string name;
int health;
public:
Player(string n, int h) : name(n), health(h) {
cout << name << " created" << endl;
}
~Player() {
cout << name << " destroyed" << endl;
}
void takeDamage(int d) {
health -= d;
cout << name << " took " << d << " damage. HP: " << health << endl;
}
string getName() const { return name; }
};
int main() {
// unique_ptr — so
...Move Semantics — Steal, Don't Copy
Copying a million-element vector means duplicating a million elements — expensive. Moving transfers the internal pointer from source to destination in constant time. The source becomes empty but valid.
Think of moving house: instead of photocopying every item (copy), you drive the moving truck to the new address (move). Same stuff, zero duplication.
string a = "Hello";
string b = move(a); // b steals a's buffer
// a is now "" (valid but empty)
// b is "Hello" — no characters were copiedMove vs. Copy
Compare expensive copy with cheap move operations
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class BigData {
vector<int> data;
string label;
public:
BigData(string l, int size) : label(l), data(size, 42) {
cout << label << " constructed (" << size << " ints)" << endl;
}
// Copy constructor (expensive)
BigData(const BigData& other) : data(other.data), label(other.label + "_copy") {
cout << label << " COPIED (expensive!)" << endl;
}
// Move constructor (c
...Common Mistakes
⚠️ Using object after move: After std::move(x), x is in a valid but unspecified state. Don't read its value.
⚠️ Copying unique_ptr: unique_ptr cannot be copied. Use std::move to transfer ownership.
⚠️ Circular shared_ptr: Two objects with shared_ptr to each other will never be freed. Break cycles with weak_ptr.
⚠️ Using new with smart pointers: Prefer make_unique and make_shared — they're exception-safe and more efficient.
Pro Tips
💡 Default to unique_ptr: Start with unique_ptr and only upgrade to shared_ptr if you genuinely need shared ownership.
💡 Mark move ops noexcept: Move constructors should be noexcept so STL containers can use them during reallocation.
💡 Rule of Five: If you write any of destructor, copy constructor, copy assignment, move constructor, or move assignment — write all five.
📋 Quick Reference
| Operation | Syntax |
|---|---|
| Create unique_ptr | auto p = make_unique<T>(args); |
| Create shared_ptr | auto p = make_shared<T>(args); |
| Transfer ownership | auto b = move(a); |
| Check null | if (ptr) or if (!ptr) |
| Reference count | ptr.use_count() |
| Weak lock | if (auto sp = weak.lock()) |
Lesson Complete!
You now know how to use smart pointers and move semantics to write safe, efficient C++. Next, you'll dive deeper into the C++ Memory Model — understanding how the compiler and CPU manage memory at a low level.
Sign up for free to track which lessons you've completed and get learning reminders.