std::shared_ptr and std::weak_ptr
Strong and weak counts, lock() and expiry, the shared-ownership costs, and the reference-cycle trap.
std::shared_ptr<T> keeps a control block with two counters: strong (how
many shared_ptrs own the object) and weak (how many weak_ptrs watch
it). The object dies when the strong count hits zero; the control block
dies when both do.
#include <memory>
auto a = std::make_shared<Node>(); // strong = 1
auto b = a; // strong = 2
b.reset(); // strong = 1
a.reset(); // strong = 0 โ destructor runs
std::weak_ptr<T> observes without owning. It does not keep the object
alive; it can ask whether the object still exists:
std::weak_ptr<Node> w = a;
if (auto locked = w.lock()) { // atomic upgrade to shared_ptr
locked->use(); // object is alive during this block
} else {
// object already gone
}
Use shared_ptr only when ownership is genuinely shared across unknown
lifetimes (caches, observers, graphs). It costs an atomic counter and can
hide who releases what โ unique_ptr stays the default.
The famous trap: two nodes holding shared_ptr to each other form a
reference cycle; strong counts never reach zero; both leak. The fix is
one direction becoming weak_ptr โ child points to parent weakly.