C++ Smart Pointers & Breaking circular references with weak_ptr [#33-3]
이 글의 핵심
Break reference cycles with weak_ptr; use lock() for temporary shared ownership.
Introduction: leaks from cycles
Real scenarios
MMORPG guild ↔ character, GUI observer lists, resource caches, parent/child graphs, plugin managers—any shared_ptr back-edge can create a cycle. Think of shared_ptr as a self-cleaning lease: when the strong count hits zero, the object goes away. A cycle is like two robots each holding the other’s power cord—neither refcount drops, so neither runs its destructor.
weak_ptr observes without incrementing strong count; lock() upgrades temporarily when you need a short-lived shared_ptr.
Topics: refcount recap, cycle mechanics, lock/expired, observer/cache patterns, mistakes, performance notes, production patterns.
Cycles
A→B→A with shared_ptr ⇒ counts never reach 0. Minimal pattern: struct A { shared_ptr<B> b; }; struct B { shared_ptr<A> a; };—break one edge with weak_ptr<A> on the side that should not own strongly (often the “back” or observer link).
weak_ptr usage
if (auto p = w.lock()) { ... } is the idiomatic pattern—never w.lock()->foo() without checking.
Patterns
- Observer: store
weak_ptr<Observer>, pruneexpired()entries. - Cache:
unordered_map<string, weak_ptr<Resource>>, reload onlock()failure.
Pitfalls
Use-after-free if you ignore lock() failure; expired() alone is racy—prefer lock(); don’t store shared_ptr from lock() forever if you wanted non-owning observers.
Performance
lock() uses atomics—similar cost to copying shared_ptr, but avoids keeping objects alive unintentionally.
Keywords
weak_ptr, circular reference, shared_ptr leak, observer pattern