Back to Curriculum

Memory Management and Smart Pointers

📚 Lesson 6 of 10 ⏱️ 45 min

Memory Management and Smart Pointers

45 min

Smart pointers (introduced in C++11) automatically manage dynamic memory, eliminating many common memory management errors. Unlike raw pointers, smart pointers handle memory deallocation automatically when objects go out of scope or are no longer needed. This eliminates memory leaks, double deletions, and dangling pointer issues that plague programs using raw pointers and manual memory management.

unique_ptr provides exclusive ownership of a dynamically allocated object. Only one unique_ptr can own an object at a time, and ownership can be transferred using move semantics. When the unique_ptr is destroyed, it automatically deletes the owned object. unique_ptr is lightweight (no reference counting overhead) and is the preferred smart pointer when single ownership is sufficient. It's a direct replacement for raw pointers in most cases.

shared_ptr enables shared ownership of objects through reference counting. Multiple shared_ptr instances can point to the same object, and the object is deleted only when the last shared_ptr is destroyed. Reference counting adds overhead but enables flexible ownership models. shared_ptr is useful when multiple parts of code need to share access to the same object with uncertain lifetimes.

weak_ptr provides non-owning references to objects managed by shared_ptr. weak_ptr doesn't affect the reference count, preventing circular references that would prevent objects from being deleted. Circular references occur when objects hold shared_ptr to each other, creating a cycle that prevents cleanup. weak_ptr breaks these cycles, enabling proper resource management in complex object relationships.

RAII (Resource Acquisition Is Initialization) is a fundamental C++ idiom where resource management is tied to object lifetime. Resources (memory, file handles, locks) are acquired in constructors and released in destructors. Since destructors are called automatically when objects go out of scope (even during exceptions), RAII ensures proper cleanup. Smart pointers are the primary RAII mechanism for memory management.

Modern C++ best practices strongly favor smart pointers over raw pointers for dynamic memory. Raw pointers should be used only for non-owning references (observing pointers) or when interfacing with C code. The make_unique and make_shared functions (C++11/14) are preferred for creating smart pointers, as they provide exception safety and are more efficient than using new directly.

Key Concepts

  • Smart pointers automatically manage dynamic memory.
  • unique_ptr provides exclusive ownership with no overhead.
  • shared_ptr enables shared ownership through reference counting.
  • weak_ptr prevents circular references in shared ownership.
  • RAII ties resource management to object lifetime.

Learning Objectives

Master

  • Using unique_ptr for exclusive ownership
  • Implementing shared_ptr for shared ownership
  • Using weak_ptr to break circular references
  • Applying RAII principles for resource management

Develop

  • Understanding modern C++ memory management
  • Avoiding memory leaks and dangling pointers
  • Designing resource-safe C++ code

Tips

  • Prefer unique_ptr over raw pointers for owned memory.
  • Use make_unique/make_shared instead of new: auto ptr = std::make_unique<Type>();
  • Use weak_ptr to break circular references between shared_ptr objects.
  • Don't use shared_ptr unless you truly need shared ownership (overhead).

Common Pitfalls

  • Using raw pointers for owned memory, causing memory leaks.
  • Creating circular references with shared_ptr, preventing cleanup.
  • Not understanding ownership semantics, using wrong smart pointer type.
  • Mixing raw pointers with smart pointers incorrectly.

Summary

  • Smart pointers automatically manage memory, preventing leaks.
  • unique_ptr is preferred for single ownership scenarios.
  • shared_ptr enables shared ownership but has overhead.
  • RAII ensures proper resource cleanup through object lifetime.

Exercise

Demonstrate smart pointers and RAII principles.

#include <iostream>
#include <memory>
#include <vector>

class Resource {
private:
    std::string name;
    
public:
    Resource(const std::string& n) : name(n) {
        std::cout << "Resource " << name << " created" << std::endl;
    }
    
    ~Resource() {
        std::cout << "Resource " << name << " destroyed" << std::endl;
    }
    
    void use() const {
        std::cout << "Using resource: " << name << std::endl;
    }
};

int main() {
    // unique_ptr - exclusive ownership
    auto ptr1 = std::make_unique<Resource>("Unique Resource");
    ptr1->use();
    
    // Transfer ownership
    auto ptr2 = std::move(ptr1);
    if (!ptr1) {
        std::cout << "ptr1 is now null" << std::endl;
    }
    
    // shared_ptr - shared ownership
    auto shared1 = std::make_shared<Resource>("Shared Resource");
    auto shared2 = shared1; // Reference count increases
    
    std::cout << "Reference count: " << shared1.use_count() << std::endl;
    
    // weak_ptr - weak reference
    std::weak_ptr<Resource> weak = shared1;
    std::cout << "Weak reference count: " << weak.use_count() << std::endl;
    
    return 0;
}

Exercise Tips

  • Compare ownership: unique_ptr vs shared_ptr for different scenarios.
  • Use make_unique: auto ptr = std::make_unique<Resource>("name");
  • Transfer ownership: auto ptr2 = std::move(ptr1);
  • Check weak_ptr: if (auto shared = weak.lock()) { /* use shared */ }.

Code Editor

Output