Smart Pointers
cpp1-5's own Challenge 2 built a leak on purpose: a Circle* allocated with new, never deleted, because RAII only protects objects whose own lifetime is tied to a scope — and a raw pointer variable's scope has nothing to do with the heap object it points at. This chapter is the real fix.
The Gap Raw Pointers Leave Open
A raw pointer going out of scope destroys only the pointer itself, never the object it points to. Smart pointers close this gap by wrapping the raw pointer inside an RAII-managed object — exactly cpp1-5's own idea, applied specifically to ownership.
unique_ptr — Sole Ownership
Exactly one unique_ptr owns a given object at a time. When it's destroyed, it automatically deletes the object it owns — genuinely just cpp1-5's own IntArray wrapper, generalized and already written for you.
unique_ptr Cannot Be Copied
A real, deliberate constraint: copying a unique_ptr would mean two owners both believing they're responsible for deleting the same object — exactly the double-free class from c2-3's own catalog. The compiler enforces this directly; attempting to copy one is a compile error, not a runtime risk.
Ownership can be transferred via std::move — a direct preview of cpp2-5's own Move Semantics chapter. After the move, the source unique_ptr becomes empty.
shared_ptr — Shared Ownership via Reference Counting
Multiple shared_ptrs can jointly own the same object, tracked by a hidden reference count. The object is deleted only when the last owning shared_ptr is destroyed.
The Real Comparison to Rust
unique_ptr is essentially Rust's Box<T> — sole ownership, moved not copied, deterministic destruction. shared_ptr is essentially Rust's Rc<T> — reference-counted shared ownership, the identical strategy. This is genuinely another point of real convergence, alongside cpp1-5's RAII/Drop parallel and cpp2-1's monomorphization parallel — C++'s and Rust's own ownership models visibly converge here.
A Genuine Gap Even Here — No Compile-Time Enforcement
The honest caveat: nothing stops a programmer from also keeping a raw pointer to the object a unique_ptr owns, and using that raw pointer after the unique_ptr is destroyed — a genuine use-after-free, still fully possible. Smart pointers eliminate the forgetful class of memory bug — nobody forgets to call delete anymore — not the deliberate-misuse class. Rust's borrow checker closes both.
| Concept | Raw pointer | unique_ptr | shared_ptr | Rust |
|---|---|---|---|---|
| Ownership | none — just an address | sole owner | shared, ref-counted | Box<T> / Rc<T> |
| Copy behavior | freely copyable | compile error — must move | copyable — increments the count | move / Rc::clone |
| Compile-time misuse prevention | none | prevents double-ownership only | prevents double-ownership only | prevents use-after-free entirely |
unique_ptr first — reserve shared_ptr for cases where genuine shared ownership is actually needed, since reference counting carries real runtime overhead unique_ptr doesn't have at all.
shared_ptr to the other never reach a zero reference count between them — a genuine memory leak, smart pointers notwithstanding. std::weak_ptr exists specifically to break such cycles, a non-owning reference that doesn't contribute to the count.
Coding Challenges
Rewrite cpp1-5's own Challenge 2 (a Circle allocated with new, never deleted) using std::unique_ptr instead, adding a destructor message to Circle to confirm it's now genuinely destroyed automatically.
📄 View solutionCreate two shared_ptrs jointly owning the same object, print the reference count after each is created (use_count()), then let one go out of scope and print the count again to observe it decrease.
📄 View solutionExplain precisely why smart pointers are described as eliminating the "forgetful" class of memory bug but not the "deliberate misuse" class, giving a concrete scenario where a raw pointer kept alongside a unique_ptr still produces a genuine use-after-free.
📄 View solutionChapter 4 Quick Reference
std::unique_ptr<T>— sole ownership, cannot be copied, transferred only viastd::movestd::shared_ptr<T>— reference-counted shared ownership, deleted when the last owner is destroyed- unique_ptr ≈ Rust's Box<T>; shared_ptr ≈ Rust's Rc<T> — a real convergence in ownership model
- Smart pointers eliminate forgotten-
deletebugs, not deliberate raw-pointer misuse — Rust's borrow checker closes both std::weak_ptr— a non-owning reference, the escape hatch forshared_ptrreference cycles- Next chapter: move semantics and rvalue references — the mechanism
std::movealready previewed here