Menu

C++ Dynamic Memory: new and delete Explained

How to allocate memory at runtime with new, free it with delete, and avoid the leaks, dangling pointers, and double-frees that come with managing the heap by hand.

This page includes runnable editors - edit, run, and see output instantly.

Why You'd Ask for Memory at Runtime

So far every variable you've made has lived on the stack: its size is known at compile time and it's destroyed automatically when its scope ends. That's fast and safe, but it can't handle the case where you don't know how much memory you need until the program is running - a buffer sized by user input, a structure that has to outlive the function that created it, or a graph whose shape isn't known ahead of time.

For those cases C++ lets you allocate from the heap (also called the free store) with new, and hand it back with delete. The new expression reserves a block, runs the constructor, and returns a pointer to it - building directly on the pointers you saw on the previous page.

The variable p itself lives on the stack - it's just a pointer. The int it points to lives on the heap and stays alive until you delete it, no matter how many scopes come and go.

Stack vs Heap

The distinction is the whole reason new exists, so it's worth making concrete.

void demo() {
    int a = 10;            // on the stack - gone when demo() returns
    int* b = new int(10);  // 'b' on the stack, the int it points to on the heap
}                          // 'a' destroyed; the heap int LEAKS - never deleted

Key differences:

  • Stack - automatic lifetime, very fast, limited size (typically a few MB), freed for you when the scope exits.
  • Heap - manual lifetime, slightly slower, large, and freed only when you call delete.

The trade is flexibility for responsibility: heap memory lives exactly as long as you want, but you become the one who must remember to free it.

Allocating Arrays with new[]

When you need a block whose length is decided at runtime, use the array form new T[n]. It returns a pointer to the first element, and you free it with the matching delete[].

The rule is strict and easy to get wrong: memory from new is freed with delete, and memory from new[] is freed with delete[]. Mixing them - delete arr on something allocated with new[] - is undefined behavior, even if it appears to work on your machine.

The Three Classic Bugs

Manual memory management has a small set of mistakes that account for most heap bugs. Learn to recognize all three.

1. Memory leak - you never call delete. The block stays reserved forever. Harmless once, fatal in a loop.

void leaky() {
    int* p = new int(5);
    // ... no delete ...
}   // p is gone; the heap int is now unreachable AND unfreed

2. Dangling pointer - you use memory after freeing it. The pointer still holds the old address, but that memory is no longer yours.

3. Double-free - you delete the same block twice. This corrupts the heap's bookkeeping and usually crashes.

int* p = new int(1);
delete p;
delete p;   // double-free - undefined behavior, often a crash

Setting a pointer to nullptr after deleting it defuses both dangling use and double-free: dereferencing nullptr crashes immediately (easy to debug), and delete nullptr is explicitly a safe no-op.

A Realistic Allocate-Use-Free Cycle

Putting it together, here's the shape of correct manual management: allocate, use, free exactly once, and don't touch the pointer afterward.

Notice delete u does two things for a class type: it runs the object's destructor first, then releases the raw memory. That ordering matters once your objects own resources of their own.

A subtle gotcha: if an exception is thrown between new and delete, the delete never runs and you leak. Wrapping every allocation in try/catch to handle that is tedious and error-prone - which is exactly the problem the next page solves.

Next: Smart Pointers

You've now seen the full cost of doing memory by hand: every new is a promise to delete later, and a single missed, doubled, or early free is undefined behavior. Modern C++ almost never makes that promise manually. The next page introduces smart pointers - std::unique_ptr and std::shared_ptr - objects that own a heap allocation and call delete for you automatically when they go out of scope, turning all three classic bugs into things the compiler and RAII handle on your behalf.

Frequently Asked Questions

What is the difference between new and delete in C++?

new allocates memory on the heap at runtime and returns a pointer to it; delete frees memory that was allocated with new. Every new must be matched by exactly one delete, or you leak memory. For arrays, use new[] with delete[].

What happens if you forget to call delete in C++?

You get a memory leak: the heap block stays reserved for your program's whole lifetime even though nothing points to it anymore. One leak is usually harmless, but leaks in a loop or a long-running service grow until the program runs out of memory and crashes.

Should I use new and delete directly in modern C++?

Rarely. Prefer containers like std::vector or smart pointers (std::unique_ptr, std::shared_ptr) that free memory automatically. Raw new/delete is worth understanding because smart pointers wrap it, but in everyday code it's a source of leaks and dangling pointers.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED