Skip to content

Memory Management

Managing memory efficiently and safely is one of C++'s core strengths. Modern C++ automates much of this using RAII and Smart Pointers, making manual new and delete largely obsolete.

Stack vs. Heap

Understanding where your data lives is crucial.

The Stack

  • Fast: Allocation is just moving a pointer.
  • Automatic: Variables are destroyed when they go out of scope.
  • Limited Size: Large arrays can cause a stack overflow.
1
2
3
4
void func() {
    int x = 10; // On the stack
    int array[100]; // On the stack
} // x and array are automatically destroyed here

The Heap (Free Store)

  • Flexible: You control the lifetime.
  • Large: Limited only by system memory.
  • Slower: Allocation involves finding free blocks.
  • Manual Management: Requires explicit cleanup (or smart pointers).

Pointers vs. References

Both refer to data elsewhere in memory, but they have key differences.

References (&)

  • Must be initialized when declared.
  • Cannot be null.
  • Cannot be reseated (cannot refer to a different object later).
  • Syntax: Use like a normal variable.
1
2
3
int a = 10;
int& ref = a;
ref = 20; // a is now 20

Pointers (*)

  • Can be uninitialized (dangerous!).
  • Can be null (nullptr).
  • Can be reseated.
  • Syntax: Use * to dereference (access value) and -> to access members.
1
2
3
4
int a = 10;
int* ptr = &a; // ptr holds the address of a
*ptr = 20;     // a is now 20
ptr = nullptr; // ptr now points to nothing

RAII (Resource Acquisition Is Initialization)

This is the most important idiom in C++. It binds the life cycle of a resource (memory, file, lock) to the life cycle of an object.

  • Constructor: Acquires the resource.
  • Destructor: Releases the resource.

Because stack objects are destroyed automatically, RAII guarantees no resource leaks, even if exceptions are thrown.

Smart Pointers (Modern C++)

Rule: Never use new and delete (and definitely never malloc/free). Use smart pointers from <memory>.

std::unique_ptr

The default choice. Represents exclusive ownership.

  • Usage: Use std::make_unique<T>() to create.
  • Transfer: Can be moved (std::move), but not copied.
1
2
3
4
5
6
7
#include <memory>

void process() {
    // Preferred: make_unique (C++14)
    auto ptr = std::make_unique<MyClass>();
    ptr->do_something();
} // ptr is destroyed, MyClass is deleted automatically

std::shared_ptr

Use only when ownership is truly shared (e.g., a node in a graph referenced by multiple parents).

  • Usage: Use std::make_shared<T>().
  • Overhead: Keeps a reference count.
auto ptr1 = std::make_shared<MyClass>();
auto ptr2 = ptr1; // Ref count = 2

std::weak_ptr

A non-owning observer for shared_ptr. Breaks cyclic references (e.g., Parent <-> Child).

Raw Pointers (*) in Modern C++

Raw pointers are not dead, but their role has changed. - Owning: std::unique_ptr / std::shared_ptr - Non-owning (Observer): Raw pointer T* or Reference T&.

If a function just needs to use an object, pass it by reference or raw pointer. Do not pass smart pointers unless you are manipulating ownership.

Move Semantics (C++11)

Move semantics allow resources to be transferred rather than copied. This is a massive performance optimization.

Lvalues vs. Rvalues

  • Lvalue: Has a name and an address (e.g., x).
  • Rvalue: Temporary value (e.g., 10, x + y, return value of a function).

std::move

Casts an lvalue to an rvalue, enabling the move constructor/assignment.

1
2
3
4
5
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); 

// v2 now owns the data. 
// v1 is empty (valid but unspecified state).

Deep Copy vs. Shallow Copy

  • Shallow Copy: Copies pointer values. Both objects point to the same memory. (Dangerous if not handled carefully).
  • Deep Copy: Allocates new memory and copies the actual data. (Safer but slower).

Smart pointers and STL containers handle this correctly automatically.

Best Practices

  1. Prefer Stack: Use stack variables whenever possible.
  2. Use std::unique_ptr: By default for heap allocation.
  3. Use std::shared_ptr: Only when ownership is truly shared.
  4. Avoid new/delete: They are error-prone.