Pointers and Memory Management
Smart Pointers
unique_ptr, shared_ptr, weak_ptr
Interview: Modern C++ essential
Smart Pointers
Smart pointers (C++11, <memory>) automate memory management by wrapping raw pointers and deleting them automatically when they go out of scope. They implement RAII and eliminate the most common memory bugs: leaks, double-free, and use-after-free.
std::unique_ptr
Exclusive ownership — only one unique_ptr can own an object at a time. Non-copyable, move-only. Zero overhead over raw pointer. Use as the default smart pointer for owned heap objects.
std::shared_ptr
Shared ownership via reference counting. Destructs the object when the last shared_ptr to it is destroyed. Thread-safe reference count. Overhead: atomic increment/decrement on copy, plus separate control block allocation.
std::weak_ptr
Non-owning observer of a shared_ptr. Doesn't affect reference count. Must be converted to shared_ptr (lock()) before use — returns nullptr if object was already destroyed. Breaks shared_ptr cyclic reference cycles.
Interview Corner
Q: What is a circular reference with shared_ptr and how do you fix it?
A: If A holds a shared_ptr to B and B holds a shared_ptr to A, neither's reference count reaches zero — memory leak. Fix: one of the pointers should be a weak_ptr. The object that logically "doesn't own" the other holds a weak_ptr. When it needs to use the object, it locks the weak_ptr to get a temporary shared_ptr.
Q: Why prefer make_unique/make_shared over direct new?
A: (1) Exception safety: f(shared_ptr(new T), g()) — if g() throws after new but before shared_ptr construction, memory leaks. make_shared eliminates this. (2) make_shared performs a single allocation for both the object and control block (better cache locality, one fewer allocation).
Common Pitfalls
- Creating shared_ptr from raw this: Using
shared_ptr<T>(this)inside a class creates a second control block — double-free. Inherit fromstd::enable_shared_from_thisand useshared_from_this()instead. - Overusing shared_ptr: Using shared_ptr everywhere adds unnecessary reference counting overhead. Default to unique_ptr; use shared_ptr only when shared ownership is genuinely needed.
Best Practices
- Default to
std::unique_ptr. Upgrade to shared_ptr only when shared ownership is explicitly required. - Always use
std::make_uniqueandstd::make_shared— never raw new/delete.