C++ Smart Pointers & Breaking circular references with weak_ptr [#33-3]

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>, prune expired() entries.
  • Cache: unordered_map<string, weak_ptr<Resource>>, reload on lock() 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