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?
Practice Playground
Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.
Output:
Error:
Lesson Progress
- Fix This Code
- Quick Quiz
- Practice Playground - run once