Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The new operator in C++ is a language construct responsible for dynamic memory allocation and object initialization on the free store. When invoked, it performs a strict two-phase operation: it first allocates raw memory of the exact size and alignment required by the specified type by calling an allocation function, and subsequently invokes the appropriate constructor to initialize the object within that memory space. It returns a strongly-typed pointer to the newly created object.

Standard Syntax

The new operator can be used to allocate single objects or contiguous arrays of objects.
#include <iostream>

struct Widget {
    int x;
    int y;
    Widget() : x(0), y(0) {}
    Widget(int a, int b) : x(a), y(b) {}
};

int main() {
    // Default initialization: primitive types are default-initialized (leaving an indeterminate value); 
    // class types call their default constructor.
    int* ptr1 = new int;
    Widget* w1 = new Widget;

    // Value initialization: primitive types are zero-initialized.
    int* ptr2 = new int();

    // Direct initialization with arguments.
    Widget* w2 = new Widget(10, 20);

    // Uniform initialization (C++11 and later).
    Widget* w3 = new Widget{30, 40};

    // Array allocation: primitive types are default-initialized (indeterminate values); 
    // class types call the default constructor per element.
    int* arrPtr = new int[5];

    // Memory management contract fulfillment
    delete ptr1;
    delete w1;
    delete ptr2;
    delete w2;
    delete w3;
    delete[] arrPtr;

    return 0;
}

The Two-Phase Execution Model

Unlike C’s malloc(), which only allocates raw bytes, the C++ new expression is type-aware and lifecycle-aware.
  1. Allocation Phase: The compiler generates a call to an allocation function. For single objects, this is the global or class-specific void* operator new(std::size_t size). For array allocations (new Type[...]), the compiler generates a call to void* operator new[](std::size_t size). Furthermore, for over-aligned types (C++17 and later), the compiler calls the alignment-aware overload void* operator new(std::size_t, std::align_val_t) (or its array equivalent). These functions request raw, uninitialized memory from the C++ runtime’s dynamic allocator (the free store), which manages the memory pool.
  2. Initialization Phase: If the allocation succeeds, the compiler invokes the constructor of the target type on the newly acquired memory address, passing any provided arguments.
Exception-Safety Guarantee: If the constructor throws an exception during the initialization phase, the C++ runtime automatically intercepts it and calls the corresponding operator delete (or operator delete[]) to free the allocated raw memory before propagating the exception outward. This strict rollback mechanism prevents memory leaks when object instantiation fails.

Failure Behavior

By default, if the allocation phase fails (e.g., due to memory exhaustion), the new operator does not return a null pointer. Instead, it throws an exception of type std::bad_alloc.

Variants of the new Operator

C++ provides overloaded forms of the new operator to alter its default allocation behavior.

1. Non-Throwing new

If exception handling is disabled or undesirable, the std::nothrow constant can be passed to force the operator to return nullptr upon failure instead of throwing an exception.
#include <new>
#include <iostream>

struct Widget { 
    int data; 
};

int main() {
    // Returns nullptr if allocation fails on the free store
    Widget* ptr = new (std::nothrow) Widget{42};

    if (!ptr) {
        std::cerr << "Allocation failed\n";
        return 1;
    }

    delete ptr;
    return 0;
}

2. Placement new

Placement new separates the initialization phase from the allocation phase. It allows the construction of an object at a specific, pre-allocated memory address. It bypasses operator new’s memory allocation step entirely and only executes the constructor at the provided pointer.
#include <new>
#include <iostream>

struct Widget {
    int data;
    Widget(int val) : data(val) {}
    ~Widget() {}
};

int main() {
    // 1. Pre-allocate raw memory (e.g., on the stack to bypass the free store)
    alignas(Widget) char buffer[sizeof(Widget)];

    // 2. Construct the object directly into the pre-allocated buffer
    Widget* placedPtr = new (buffer) Widget(100);

    // 3. Explicitly call the destructor (do not use delete, as memory was not allocated via the free store)
    placedPtr->~Widget();

    return 0;
}

Memory Management Contract

Every successful invocation of the standard new operator establishes a contract that must be fulfilled by a corresponding delete operator to release the memory back to the C++ runtime’s free store.
  • Objects allocated with new must be destroyed and deallocated using delete.
  • Arrays allocated with new[] must be destroyed and deallocated using delete[].
(Note: Placement new does not use delete; the destructor must be called explicitly, and the underlying raw memory is managed by the scope or custom allocator that originally provided it).
Master C++ with Deep Grasping Methodology!Learn More