Step 5 of 5
weak_ptr
shared_ptr has one weakness: cycles. If object A holds a shared_ptr to B and B holds one back to A, each keeps the other's count above zero, so neither is ever deleted. That's a memory leak.
std::weak_ptr solves it. It watches a shared object without owning it: it doesn't add to the count and doesn't keep the object alive. To use the object, call lock(). You get a shared_ptr that's valid while you hold it, or an empty one if the object is already gone.
#include <iostream>
#include <memory>
#include <string>
struct Room {
std::string name;
};
void check(const std::weak_ptr<Room>& w) {
if (auto r = w.lock()) {
std::cout << "still there: " << r->name << " (owners " << r.use_count() << ")\n";
} else {
std::cout << "room is gone\n";
}
}
int main() {
std::weak_ptr<Room> last_visited;
{
auto kitchen = std::make_shared<Room>(Room{"kitchen"});
last_visited = kitchen; // does not add an owner
std::cout << "owners " << kitchen.use_count() << "\n";
check(last_visited);
} // the only owner dies here
check(last_visited);
std::cout << "expired: " << last_visited.expired() << "\n";
}
owners 1
still there: kitchen (owners 2)
room is gone
expired: 1
How it works
- Assigning a
shared_ptrto aweak_ptrmakes it watch the object. The count stays 1. if (auto r = w.lock())declaresrand tests it in one step. Inside theif,ris a real owner, which is why the count shows 2 there: the object can't vanish while you're using it.- Once the last real owner is gone,
lock()returns an empty pointer andexpired()is true.
Where it's used
- Breaking cycles: a child points to its parent with a
weak_ptr, while the parent owns the child with ashared_ptr. - Caches and observers: "remember this object if it's still around, but don't keep it alive just for me".
Your turn: write std::string describe(const std::weak_ptr<std::string>& w) returning "tracking NAME" if the object is alive and "lost" otherwise.