C++ Dynamic Memory Management with new and delete

Memory Layout of a C/C++ Process

Before diving in to allocation APIs, it helps to know where objects live.

int globalVal = 1;
static int staticGlobalVal = 1;

void demo()
{
    static int staticVal = 1;
    int localVal = 1;
    int nums[10] = {1, 2, 3, 4};
    char buf[] = "abcd";
    const char* literal = "abcd";
    int* p1 = (int*)malloc(sizeof(int) * 4);
    int* p2 = (int*)calloc(4, sizeof(int));
    int* p3 = (int*)realloc(p2, sizeof(int) * 4);
    free(p1);
    free(p3);
}

Quick sizing quiz:

  • sizeof(nums) = 40 bytes
  • sizeof(buf) = 5 bytes (4 chars + NUL)
  • strlen(buf) = 4
  • sizeof(literal) = 4 or 8 (pointer size)
  • sizeof(p1) = 4 or 8 (pointer size)

Classic C Allocation Primitives

int* a = (int*)malloc(sizeof(int));        // uninitialized
int* b = (int*)calloc(4, sizeof(int));   // zero-initialized
int* c = (int*)realloc(b, 10*sizeof(int)); // resize; may move
free(a);
free(c);   // note: no need to free(b) if realloc moved it

Key differences

  • malloc – raw byte count, indeterminate content.
  • calloc – element count × size, zeroed bytes.
  • realloc – grow/shrink; returns new pointer if block moved.

C++ Allocation: new / delete

Built-in Types

int* p1 = new int;          // uninitialized
int* p2 = new int(42);      // initialized to 42
int* p3 = new int[5];       // array of 5 ints (uninitialized)
int* p4 = new int[5]{1,2};  // aggregate-init, rest zero

delete p1;
delete p2;
delete[] p3;
delete[] p4;

Always match new with delete and new[] with delete[].

User-Defined Types

class Widget {
public:
    Widget(int v = 0) : val(v) { std::cout << "Ctor " << val << '\n'; }
    ~Widget() { std::cout << "Dtor " << val << '\n'; }
private:
    int val;
};

Widget* w1 = (Widget*)malloc(sizeof(Widget)); // no construction
Widget* w2 = new Widget(99);                  // allocates + constructs
free(w1);                                     // no destruction
delete w2;                                    // destructs + deallocates

For arrays:

Widget* wa = new Widget[3];   // 3 default constructions
delete[] wa;                  // 3 destructions

Under the Hood: operator new / operator delete

new and delete are operators; the actual work is delegated to:

void* operator new(std::size_t n);        // throws std::bad_alloc on failure
void  operator delete(void*) noexcept;

These functions are merely thin wrappers around malloc/free that add exception-safety and bookkeeping. You can overload them for class-specific allocation strategies.

Array Cookie

When allocating new T[n] for a type with a non-trivial destructor, most implementations prepend a hidden cookie storing n. delete[] reads this cookie to know how many destructors to invoke. Omitting [] or mismatching leads to undefined behaviour.

Placement new

Construct an object in already-obtained storage:

#include <new>

char buffer[sizeof(Widget)];
Widget* pw = new (buffer) Widget(123);  // placement new
pw->~Widget();                        // explicit destruction

Useful for memory pools or containers that manage their own backing store.

malloc/free vs new/delete – Cheat Sheet

Memory Leaks

Definition: Memory that is no longer reachable but has not been released.

Consequences: Process RSS grows, swap thrashing, eventual OOM kill.

void leak_demo()
{
    int* p = new int[1000];
    // forgot delete[] p;
}

Leak Categories

  • Heap leaknew/malloc without matching delete/free.
  • Resource leak – Sockets, file descriptors, GDI handles, etc.

Detection & Prevention

  • Windows: _CrtDumpMemoryLeaks() at exit (coarse).
  • Linux: valgrind --leak-check=full ./a.out.
  • Use RAII / smart pointers (std::unique_ptr, std::shared_ptr).
  • Adopt container classes (std::vector, std::string) instead of raw arrays.
  • Establish code-review rules: every new must have an owner with clear lifetime.

Tags: C++ new delete malloc free

Posted on Sat, 16 May 2026 12:08:13 +0000 by ESCForums.com