Smart Pointers - weak_ptr
Master weak_ptr to break circular references and safely observe shared objects
Learn how to use weak_ptr to break circular references and safely observe objects managed by shared_ptr without affecting their lifetime.
A Simple Example
#include <iostream>
#include <memory>
#include <string>
class Node {
public:
std::string data;
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev;
explicit Node(const std::string& d) : data{d} {
std::cout << "Node " << data << " created\n";
}
~Node() {
std::cout << "Node " << data << " destroyed\n";
}
};
int main() {
auto node1{std::make_shared<Node>("A")};
auto node2{std::make_shared<Node>("B")};
node1->next = node2;
node2->prev = node1;
std::cout << "Reference count for A: " << node1.use_count() << "\n";
std::cout << "Reference count for B: " << node2.use_count() << "\n";
// Access through weak_ptr
if (auto prev{node2->prev.lock()}) {
std::cout << "B's previous node: " << prev->data << "\n";
}
return 0;
} // Both nodes properly destroyed despite circular structure
Breaking It Down
Non-Owning Reference
- weak_ptr observes an object but doesn't own it
- Does NOT increase reference count when created
- Object can be deleted even if weak_ptrs exist
- Remember: weak_ptr is for observation, not ownership
Breaking Circular References
- Problem: A->B (shared_ptr) and B->A (shared_ptr) = both never deleted
- Solution: A->B (shared_ptr) and B->A (weak_ptr) = both properly cleaned up
- Common pattern: Parent owns children with shared_ptr, children reference parent with weak_ptr
- Remember: At least one direction must be weak_ptr to break the cycle
Using lock() to Access
- Cannot access object directly through weak_ptr
-
Must call
lock()to get a temporary shared_ptr - Returns nullptr if object has been deleted
-
Pattern:
if (auto ptr = weak.lock()) { use ptr } - Remember: Always check the returned shared_ptr before using
Checking Object Lifetime
-
expired()returns true if object has been deleted -
lock()returns nullptr if object is gone - weak_ptr can safely detect when shared object dies
- Remember: Perfect for caches - weak_ptr won't keep cached objects alive
Why This Matters
- Circular references with shared_ptr create memory leaks - if A owns B and B owns A (both with shared_ptr), neither ever gets deleted.
- weak_ptr solves this by providing a non-owning reference that doesn't increase the reference count.
- It's perfect for caches, observers, and parent-child relationships where you need to reference an object without keeping it alive.
Critical Insight
weak_ptr can detect when an object dies. Call lock() to get a shared_ptr - if the object still exists, you get a valid shared_ptr; if it's gone, you get nullptr. This makes weak_ptr perfect for caches:
class Cache {
std::map<std::string, std::weak_ptr<Resource>> cache;
std::shared_ptr<Resource> get(const std::string& key) {
if (auto cached = cache[key].lock()) {
return cached; // Still alive!
}
// Gone, create new one
auto resource = std::make_shared<Resource>(key);
cache[key] = resource;
return resource;
}
};
Store weak_ptr to cached objects. If they've been deleted to free memory, your weak_ptr detects it automatically and you recreate them!
Best Practices
Always check lock() result: Use if (auto ptr = weak.lock()) to ensure object exists before using.
Use for parent-child: Parent owns child with shared_ptr, child references parent with weak_ptr.
Perfect for caches: Store weak_ptr to cached objects so they can be freed when memory is tight.
Break cycles: Identify circular references in your design and use weak_ptr for one direction.
Common Mistakes
Forgetting to lock(): Cannot access object directly - must call lock() first.
Not checking lock() result: Always check if lock() returns nullptr before using the shared_ptr.
Using for ownership: weak_ptr is for observation, not ownership - use shared_ptr for ownership.
Both directions weak: If both directions are weak_ptr, object can be deleted while in use.
Debug Challenge
This code causes a memory leak. Click the highlighted line to fix it:
Quick Quiz
- Does weak_ptr increase the reference count?
- How do you access the object from weak_ptr?
- What is weak_ptr's main use case?
Step Through the Code
Walk through the code step by step. Watch how variables change and see the program output at each line.
Variables
Output
Stack / Heap
Output:
Error:
Lesson Progress
- Fix This Code
- Quick Quiz
- Practice Playground - run once