Site Tools


wiki:weak-ptr

weak_ptr

std::weak_ptr is a non-owning observer of an object managed by shared_ptr: it can check whether the object still exists and temporarily obtain a shared_ptr to it, but holding a weak_ptr alone never keeps the object alive. It exists specifically to break the reference cycles that plain shared_ptr can't resolve on its own.

The cycle problem

Two objects that hold shared_ptrs to each other keep each other's strong reference count above zero forever, even if nothing else in the program refers to either of them. Neither destructor ever runs, and the memory leaks silently, no crash, no warning, just objects that are unreachable from anywhere else in the program but never freed.

struct Node {
    std::shared_ptr<Node> next;   // if two Nodes point at each other,
};                                 // neither's refcount ever reaches zero
 
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->next = b;
b->next = a;   // cycle: a and b keep each other alive forever

The standard place this shows up is a parent/child or observer relationship: a parent holding shared_ptr<Child> and each child holding shared_ptr<Parent> back creates exactly this cycle. The fix is for one direction of the relationship, typically the “back” reference (child to parent, observer to subject), to be a weak_ptr instead.

struct Node {
    std::weak_ptr<Node> parent;      // back-reference, doesn't keep parent alive
    std::shared_ptr<Node> child;     // forward-reference, owns the child
};

Using a weak_ptr

Since the observed object might have already been destroyed, a weak_ptr can't be dereferenced directly. lock() atomically checks whether the object is still alive and, if so, returns a shared_ptr to it (which itself keeps the object alive for as long as that shared_ptr is held); if the object is gone, lock() returns a null shared_ptr.

std::weak_ptr<Node> weak_parent = child->parent;
if (std::shared_ptr<Node> parent = weak_parent.lock()) {
    parent->doThing();   // safe: parent is guaranteed alive for this scope
} else {
    // parent has already been destroyed
}

This lock-then-check pattern is the whole reason weak_ptr exists as its own type rather than just a raw pointer: a raw pointer gives no way to ask “is the pointee still alive” safely, since dereferencing a dangling raw pointer is undefined behavior, while weak_ptr::lock() turns that question into a defined, atomic operation.

wiki/weak-ptr.md · Last modified: by 127.0.0.1