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 is used in a built-in C++ expression to dynamically allocate contiguous memory for an array of objects on the free store (heap). It calculates the required memory footprint, invokes the appropriate allocation function (operator new[]), sequentially initializes each element in the array, and returns a pointer to the first element.

Syntax and Initialization

A new[] expression supports several initialization strategies depending on the syntax used. The distinction between default initialization and value initialization is particularly critical for types lacking a user-provided default constructor.
#include <new>
#include <cstddef>

struct ImplicitType {
    int value; // No user-provided default constructor
};

struct ExplicitType {
    int value;
    ExplicitType() : value(0) {} // User-provided default constructor
    ExplicitType(int v) : value(v) {}
};

int main() {
    std::size_t size = 3;

    // Default initialization: 
    // ImplicitType::value is left uninitialized (indeterminate).
    // ExplicitType::value is initialized to 0 via the user-provided constructor.
    ImplicitType* ptr1 = new ImplicitType[size];
    ExplicitType* ptr2 = new ExplicitType[size];

    // Value initialization: 
    // ImplicitType is zero-initialized BEFORE the implicit constructor is called.
    // ExplicitType simply invokes the user-provided default constructor.
    ImplicitType* ptr3 = new ImplicitType[size](); 
    ExplicitType* ptr4 = new ExplicitType[size]{};

    // List-initialization (C++11): Initializes elements in order.
    // The third element is value-initialized.
    ExplicitType* ptr5 = new ExplicitType[size]{ ExplicitType(1), ExplicitType(2) };

    // Non-throwing allocation: Returns nullptr on failure instead of throwing.
    ExplicitType* ptr6 = new (std::nothrow) ExplicitType[size];

    delete[] ptr1;
    delete[] ptr2;
    delete[] ptr3;
    delete[] ptr4;
    delete[] ptr5;
    delete[] ptr6;

    return 0;
}

Execution Mechanics

When a new[] expression is evaluated, the C++ runtime performs the following sequence of operations:
  1. Size Calculation: The compiler calculates the total bytes required by multiplying the array size by sizeof(Type). For types with non-trivial destructors, the compiler typically allocates additional hidden memory (an “array cookie”) immediately preceding the returned pointer. This cookie stores the number of elements so the runtime knows exactly how many destructors to invoke during deallocation.
  2. Memory Allocation: The expression calls the global or class-specific allocation function, void* operator new[](std::size_t). This function is strictly responsible for acquiring raw, uninitialized memory from the operating system.
  3. Object Initialization: The runtime iterates through the allocated memory block and constructs each object based on the syntax provided:
    • Default initialization (new Type[size]): The default constructor is invoked. If the class lacks a user-provided default constructor, its members are left uninitialized.
    • Value initialization (new Type[size]()): For classes without a user-provided default constructor, the runtime performs zero-initialization on the object before calling the implicitly-defined default constructor. For classes with a user-provided default constructor, that constructor is simply invoked. Fundamental types are zero-initialized.
    • List-initialization (new Type[size]{...}): Elements are initialized in order. For aggregate types, this performs aggregate initialization. For non-aggregate classes, it invokes the appropriate constructor. Any remaining elements not covered by the initializer list are value-initialized.

Multi-dimensional and Zero-Sized Arrays

The new[] operator handles multi-dimensional arrays and zero-sized allocations under strict language rules:
  • Multi-dimensional Arrays: When dynamically allocating multi-dimensional arrays, only the first dimension (the outermost array) can be a runtime variable. All subsequent dimensions must be constant expressions.
  • Zero-Sized Arrays: Allocating an array of size zero is perfectly valid in C++. The new[] expression returns a distinct, non-null pointer. While this pointer cannot be safely dereferenced, it represents a valid memory allocation that must be cleaned up.
#include <cstddef>

int main() {
    std::size_t dynamic_rows = 5;

    // Valid: First dimension is a variable, second is a constant expression
    int (*matrix)[10] = new int[dynamic_rows][10];

    // Valid: Zero-sized array allocation
    int* zero_array = new int[0];

    delete[] matrix;
    delete[] zero_array;

    return 0;
}

Deallocation Requirement

Memory allocated via a new[] expression must strictly be deallocated using the delete[] operator. Using the scalar delete operator on an array pointer results in undefined behavior (UB). The C++ standard provides no guarantees about program execution in this scenario; the program may crash, corrupt the heap, or terminate before any destructors are invoked.

Exception Handling

If the allocation function fails to acquire the requested memory, the new[] expression throws a std::bad_alloc exception. If the (std::nothrow) tag is utilized, the expression suppresses the exception and instead returns a nullptr. If an exception is thrown during the initialization phase (e.g., the constructor of the 4th element throws), the C++ runtime guarantees exception safety by automatically invoking the destructors for all fully constructed elements (elements 1 through 3) in reverse order, and then deallocating the raw memory block before propagating the exception up the call stack.
Master C++ with Deep Grasping Methodology!Learn More