Smart Pointers: Modern C++ Memory Management
Smart pointers were introduced in C++11 to provide automatic memory management while maintaining the performance characteristics of C++. They use RAII to ensure proper resource cleanup and eliminate most memory-related bugs.
The Three Smart Pointers
std::unique_ptr: Exclusive ownershipstd::shared_ptr: Shared ownership with reference countingstd::weak_ptr: Non-owning observation
Unique Pointer (std::unique_ptr)
Characteristics
- Exclusive ownership: Only one
unique_ptrcan own a resource - Move semantics: Ownership can be transferred but not copied
- Zero overhead: Same performance as raw pointers
- Custom deleters: Support for custom cleanup functions
Usage Examples
// Creating unique_ptr auto ptr = std::make_unique<Widget>(args); // Transfer ownership auto ptr2 = std::move(ptr); // ptr becomes nullptr // Custom deleter auto file = std::unique_ptr<FILE, decltype(&fclose)>( fopen("data.txt", "r"), &fclose );
Shared Pointer (std::shared_ptr)
Characteristics
- Shared ownership: Multiple pointers can own the same resource
- Reference counting: Tracks how many pointers reference the object
- Thread-safe counting: Reference count operations are atomic
- Automatic cleanup: Object deleted when count reaches zero
Reference Counting Process
- Create
shared_ptr→ count = 1 - Copy
shared_ptr→ count increments - Destroy
shared_ptr→ count decrements - Count reaches 0 → object deleted
Circular Reference Problem
struct Node { std::shared_ptr<Node> next; std::weak_ptr<Node> parent; // Break cycle with weak_ptr };
Weak Pointer (std::weak_ptr)
Characteristics
- Non-owning: Doesn't affect reference count
- Observer pattern: Safely observe
shared_ptrobjects - Cycle breaking: Prevents circular references
- Expiration checking: Can detect if object still exists
Usage Pattern
std::shared_ptr<Widget> shared = std::make_shared<Widget>(); std::weak_ptr<Widget> weak = shared; if (auto locked = weak.lock()) { // Use locked as shared_ptr locked->doSomething(); } else { // Object was destroyed }
Best Practices
- Prefer
make_uniqueandmake_sharedfor creation - Use
unique_ptrby default,shared_ptrwhen sharing needed - Break cycles with
weak_ptr - Pass raw pointers to functions that don't affect ownership
- Return smart pointers from factory functions
- Use custom deleters for non-standard cleanup
Performance Considerations
unique_ptr: Zero overhead compared to raw pointersshared_ptr: Small overhead for reference countingweak_ptr: Minimal overhead, but requires lock() to accessmake_shared: More efficient thanshared_ptr(new T())
Smart pointers represent modern C++'s answer to memory management - providing safety without sacrificing performance.
