Namespaces
Variants
Views
Actions

new expression

From cppreference.com
< cpp‎ | language
Revision as of 05:12, 31 May 2020 by Fruderica (Talk | contribs)

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
new expression
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Creates and initializes objects with dynamic storage duration, that is, objects whose lifetime is not limited by the scope in which they were created.

Contents

Syntax

::(optional) new (placement_params)(optional) ( type ) initializer(optional) (1)
::(optional) new (placement_params)(optional) type initializer(optional) (2)
1) Attempts to create an object of type, denoted by the type-id type, which may be array type, and may include a placeholder type specifier(since C++11), or include a class template name whose argument is to be deduced by class template argument deduction(since C++17).
2) Same, but type cannot include parentheses:
new int(*[10])(); // error: parsed as (new int) (*[10]) ()
new (int (*[10])()); // okay: allocates an array of 10 pointers to functions

In addition, unparenthesized type is greedy: it will include every token that can be a part of a declarator:

new int + 1; // okay: parsed as (new int) + 1, increments a pointer returned by new int
new int * 1; // error: parsed as (new int*) (1)

The initializer is not optional if

auto p = new auto('c');          // creates a single object of type char. p is a char*
double* p = new double[]{1,2,3}; // creates an array of type double[3]

Explanation

The new expression attempts to allocate storage and then attempts to construct and initialize either a single unnamed object, or an unnamed array of objects in the allocated storage. The new-expression returns a prvalue pointer to the constructed object or, if an array of objects was constructed, a pointer to the initial element of the array.

If type is an array type, all dimensions other than the first must be specified as positive integral constant expression(until C++14)converted constant expression of type std::size_t(since C++14), but (only when using un-parenthesized syntax (2)) the first dimension may be an expression of integral type, enumeration type, or class type with a single non-explicit (since C++11)conversion function to integral or enumeration type(until C++14)any expression convertible to std::size_t(since C++14). This is the only way to directly create an array with size defined at runtime, such arrays are often referred to as dynamic arrays:

int n = 42;
double a[n][5]; // error
auto p1 = new  double[n][5];  // okay
auto p2 = new  double[5][n];  // error, only the first dimension may be variable
auto p3 = new (double[n][5]); // error, syntax (1) cannot be used for dynamic arrays

The behavior is undefined if the value in the first dimension (converted to integral or enumeration type if needed) is negative.

(until C++11)

In the following cases the expression specifying the first dimension is erroneous:

  • the expression is of non-class type and its value before conversion to std::size_t is negative;
  • the expression is of class type and its value after user-defined conversion function and before the second standard conversion is negative;
  • the value of the expression is larger than some implementation-defined limit;
  • the value is smaller than the number of array elements provided in the brace-enclosed initializer (including the terminating '\0' on a string literal).

If the value in the first dimension is erroneous for any of these reasons,

  • if, after conversion to std::size_t, the first dimension is a core constant expression, the program is ill-formed (a compile-time error is issued);
  • Otherwise, if the allocation function that would have been called is non-throwing, the new-expression returns the null pointer of the required result type
  • Otherwise, the new-expression does not call the allocation function, and instead throws an exception of type std::bad_array_new_length or derived from it
(since C++11)

The first dimension of zero is acceptable, and the allocation function is called.

Note: std::vector offers similar functionality for one-dimensional dynamic arrays.

Allocation

The new-expression allocates storage by calling the appropriate allocation function. If type is a non-array type, the name of the function is operator new. If type is an array type, the name of the function is operator new[].

As described in allocation function, the C++ program may provide global and class-specific replacements for these functions. If the new-expression begins with the optional :: operator, as in ::new T or ::new T[n], class-specific replacements will be ignored (the function is looked up in global scope). Otherwise, if T is a class type, lookup begins in the class scope of T.

When calling the allocation function, the new-expression passes the number of bytes requested as the first argument, of type std::size_t, which is exactly sizeof(T) for non-array T.

Array allocation may supply unspecified overhead, which may vary from one call to new to the next, unless the allocation function selected is the standard non-allocating form. The pointer returned by the new-expression will be offset by that value from the pointer returned by the allocation function. Many implementations use the array overhead to store the number of objects in the array which is used by the delete[] expression to call the correct number of destructors. In addition, if the new-expression is used to allocate an array of char, unsigned char, or std::byte, it may request additional memory from the allocation function if necessary to guarantee correct alignment of objects of all types no larger than the requested array size, if one is later placed into the allocated array.

New-expressions are allowed to elide or combine allocations made through replaceable allocation functions. In case of elision, the storage may be provided by the compiler without making the call to an allocation function (this also permits optimizing out unused new-expression). In case of combining, the allocation made by a new-expression E1 may be extended to provide additional storage for another new-expression E2 if all of the following is true:

1) The lifetime of the object allocated by E1 strictly contains the lifetime of the object allocated by E2,
2) E1 and E2 would invoke the same replaceable global allocation function
3) For a throwing allocation function, exceptions in E1 and E2 would be first caught in the same handler.

Note that this optimization is only permitted when new-expressions are used, not any other methods to call a replaceable allocation function: delete[] new int[10]; can be optimized out, but operator delete(operator new(10)); cannot.

(since C++14)

During an evaluation of a constant expression, a call to an allocation function is always omitted. Only new-expressions that would otherwise result in a call to a replaceable global allocation function can be evaluated in constant expressions.

(since C++20)
Placement new

If placement_params are provided, they are passed to the allocation function as additional arguments. Such allocation functions are known as "placement new", after the standard allocation function void* operator new(std::size_t, void*), which simply returns its second argument unchanged. This is used to construct objects in allocated storage:

char* ptr = new char[sizeof(T)]; // allocate memory
T* tptr = new(ptr) T;            // construct in allocated storage ("place")
tptr->~T();                      // destruct
delete[] ptr;                    // deallocate memory

Note: this functionality is encapsulated by the member functions of the Allocator classes.

When allocating an object whose alignment requirement exceeds __STDCPP_DEFAULT_NEW_ALIGNMENT__ or an array of such objects, the new-expression passes the alignment requirement (wrapped in std::align_val_t) as the second argument for the allocation function (for placement forms, placement_params appear after the alignment, as the third, fourth, etc arguments). If overload resolution fails (which happens when a class-specific allocation function is defined with a different signature, since it hides the globals), overload resolution is attempted a second time, without alignment in the argument list. This allows alignment-unaware class-specific allocation functions to take precedence over the global alignment-aware allocation functions.

(since C++17)
new T;      // calls operator new(sizeof(T))
            // (C++17) or operator new(sizeof(T), std::align_val_t(alignof(T))))
new T[5];   // calls operator new[](sizeof(T)*5 + overhead)
            // (C++17) or operator new(sizeof(T)*5+overhead, std::align_val_t(alignof(T))))
new(2,f) T; // calls operator new(sizeof(T), 2, f)
            // (C++17) or operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)

If the allocation function returns a null pointer, which is possible if the non-throwing overload was selected, e.g. with new(std::nothrow) T;, then the new-expression returns immediately, it does not attempt to initialize an object or to call a deallocation function. If the standard placement allocation function returns a null pointer, which is possible if the user passes a null pointer as the argument, the behavior is undefined.(since C++17)

Construction

The object created by a new-expression is initialized according to the following rules:

  • For non-array type, the single object is constructed in the acquired memory area.
  • If initializer is a brace-enclosed list of arguments, the object is list-initialized.
(since C++11)
  • If type is an array type, an array of objects is initialized.
(since C++11)
(since C++20)

If initialization terminates by throwing an exception (e.g. from the constructor), if new-expression allocated any storage, it calls the appropriate deallocation function: operator delete for non-array type, operator delete[] for array type. The deallocation function is looked up in global scope if the new-expression used the ::new syntax, otherwise it is looked up in the scope of T, if T is a class type. If the failed allocation function was usual (non-placement), lookup for the deallocation function follows the rules described in delete-expression. For a failed placement new, all parameter types, except the first, of the matching deallocation function must be identical to the parameters of the placement new. The call to the deallocation function is made the value obtained earlier from the allocation function passed as the first argument, alignment passed as the optional alignment argument(since C++17), and placement_params, if any, passed as the additional placement arguments. If no deallocation function is found, memory is not deallocated.

Memory leaks

The objects created by new-expressions (objects with dynamic storage duration) persist until the pointer returned by the new-expression is used in a matching delete-expression. If the original value of pointer is lost, the object becomes unreachable and cannot be deallocated: a memory leak occurs.

This may happen if the pointer is assigned to:

int* p = new int(7); // dynamically allocated int with value 7
p = nullptr; // memory leak

or if the pointer goes out of scope:

void f()
{
    int* p = new int(7);
} // memory leak

or due to exception

void f()
{
   int* p = new int(7);
   g();      // may throw
   delete p; // okay if no exception
} // memory leak if g() throws

To simplify management of dynamically-allocated objects, the result of a new-expression is often stored in a smart pointer: std::auto_ptr (until C++17)std::unique_ptr, or std::shared_ptr(since C++11). These pointers guarantee that the delete expression is executed in the situations shown above.

Keywords

new

Notes

Itanium C++ ABI requires that the array allocation overhead is zero if the element type of the created array is trivially destructible. So does MSVC.

Some implementations (e.g. MSVC) require non-zero array allocation overhead on non-allocating placement array new if the element type is not trivially destructible, which is no longer conforming since CWG2382.

A non-allocating placement array new-expression that creates an array of char, unsigned char, or std::byte(since C++17) can be used to implicitly create objects on given region of storage: it ends lifetime of objects overlapping with the array, and then implicitly creates objects of implicit-lifetime types in the array.

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 299 C++98 value in the first dimension must has integer or enumeration type class types permitted
CWG 1992 C++11 new (std::nothrow) int[N] could throw bad_array_new_length changed to return a null pointer
P1009R2 C++11 the array bound cannot be deduced in a new expression deduction permitted
CWG 2382 C++98 non-allocating placement array new could require allocation overhead such allocation overhead disallowed

See also