C++ by Example
C++ by Example

Dynamic Memory

2 min read

Dynamic Memory

Stack-allocated objects have lifetimes tied to their enclosing scope. When you need an object to outlive its creating function, or when its size is not known at compile time, you allocate it on the heap using new and release it with delete.

new and delete

#include <iostream>

int main() {
    int* p = new int(42);     // allocate a single int, initialize to 42
    std::cout << *p << "\n";  // 42

    *p = 100;
    std::cout << *p << "\n";  // 100

    delete p;                 // return memory to the heap
    p = nullptr;              // good practice: prevent use after free
}

After delete, the pointer is dangling — it holds an address that is no longer valid. Writing p = nullptr immediately after helps catch accidental uses.

Arrays with new[] and delete[]

#include <iostream>

int main() {
    int n = 10;
    int* arr = new int[n];   // allocate array of n ints

    for (int i = 0; i < n; i++) arr[i] = i * i;
    for (int i = 0; i < n; i++) std::cout << arr[i] << " ";
    std::cout << "\n";

    delete[] arr;            // must use delete[], not delete
    arr = nullptr;
}

Mismatching new[] with plain delete (or vice versa) is undefined behavior.

The problems with manual allocation

Manual new/delete is error-prone:

  • Memory leak: new without a matching delete — memory is never returned.
  • Double free: calling delete twice on the same pointer — undefined behavior, typically a crash or corruption.
  • Use after free: reading or writing through a pointer after delete — undefined behavior.
  • Exception safety: if an exception is thrown between new and delete, the delete is skipped.
void risky() {
    int* p = new int(42);
    might_throw();   // if this throws, p is never deleted
    delete p;
}

Placement new

Placement new constructs an object at an address you supply, without allocating:

#include <new>

char buffer[sizeof(int)];
int* p = new (buffer) int(99);   // construct in buffer
std::cout << *p << "\n";          // 99
p->~int();                        // explicitly call destructor (rarely needed for int)

Placement new is used in memory pools, arenas, and embedded systems. You are responsible for managing the memory lifetime.

std::allocator

The standard library's generic allocator separates allocation from construction:

#include <memory>

std::allocator<int> alloc;
int* p = alloc.allocate(5);   // raw memory for 5 ints

for (int i = 0; i < 5; i++) std::construct_at(p + i, i * 10);
for (int i = 0; i < 5; i++) std::cout << p[i] << " ";

for (int i = 0; i < 5; i++) std::destroy_at(p + i);
alloc.deallocate(p, 5);

Container implementations use allocators internally. Most application code never touches them directly.

Prefer smart pointers

Raw new/delete should be a last resort in new code. Use std::unique_ptr and std::shared_ptr instead — they automate deletion and are exception-safe. See the Smart Pointers chapter.

C++ by Example
C++ by Example

Learn modern C++ through working code. Each chapter introduces one concept — variables, functions, classes, templates, smart pointers, concurrency — with a clear example, a line-by-line explanation, and notes on how it applies in real programs.

View book →