Namespaces
Variants

new expression

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
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
Special member functions
Templates
Miscellaneous

Создает и инициализирует объекты с динамической storage duration , то есть объекты, время жизни которых не обязательно ограничено областью видимости, в которой они были созданы.

Содержание

Синтаксис

:: (необязательно) new ( тип  ) инициализатор-new  (необязательно) (1)
:: (необязательно) new тип инициализатор-new  (необязательно) (2)
:: (необязательно) new ( аргументы-размещения  ) ( тип  ) инициализатор-new  (необязательно) (3)
:: (необязательно) new ( аргументы-размещения  ) тип инициализатор-new  (необязательно) (4)
1,2) Попытка создания объекта типа, обозначенного type-id type , который может быть типом массива , и может включать спецификатор типа-заполнителя (начиная с C++11) , или включать имя шаблона класса, аргументы которого должны быть выведены посредством вывода аргументов шаблона класса (начиная с C++17) .
3,4) Аналогично (1,2) , но предоставляет дополнительные аргументы для функции выделения памяти, см. placement new .

Объяснение

type - целевой type-id
new-initializer - список выражений в круглых скобках или список инициализации в фигурных скобках (начиная с C++11)
placement-args - дополнительные аргументы размещения


Выражение new пытается выделить память и затем пытается создать и инициализировать либо один безымянный объект, либо безымянный массив объектов в выделенной памяти. Выражение new возвращает prvalue-указатель на созданный объект или, если был создан массив объектов, указатель на начальный элемент массива.

Синтаксис (1) или (3) требуется, если type включает круглые скобки:

new int(*[10])();    // ошибка: разбирается как (new int) (*[10]) ()
new (int (*[10])()); // корректно: выделяет массив из 10 указателей на функции

Кроме того, type разбирается жадным образом: он будет включать все токены, которые могут быть частью декларатора:

new int + 1; // корректно: разбирается как (new int) + 1, инкрементирует указатель, возвращённый new int
new int * 1; // ошибка: разбирается как (new int*) (1)

new-initializer не является опциональным, если

  • используется placeholder в type , то есть, auto или decltype ( auto ) (since C++14) , возможно в комбинации с type constraint (since C++20) ,
(since C++11)
  • используется шаблон класса в type , аргументы которого должны быть deduced .
(since C++17)
double* p = new double[]{1, 2, 3}; // создает массив типа double[3]
auto p = new auto('c');            // создает одиночный объект типа char. p является char*
auto q = new std::integral auto(1);         // OK: q является int*
auto q = new std::floating_point auto(true) // ОШИБКА: ограничение типа не выполнено
auto r = new std::pair(1, true); // OK: r является std::pair<int, bool>*
auto r = new std::vector;        // ОШИБКА: тип элемента не может быть выведен

Динамические массивы

Если type является типом массива, все размерности, кроме первой, должны быть заданы как положительные integral constant expression (until C++14) converted constant expression типа std::size_t (since C++14) , но (только при использовании неподписанных синтаксисов (2) и (4) ) первая размерность может быть выражением целочисленного типа, типа перечисления или класса с единственной неявной функцией преобразования в целочисленный тип или тип перечисления (until C++14) любым выражением, преобразуемым в std::size_t (since C++14) . Это единственный способ напрямую создать массив с размером, определяемым во время выполнения; такие массивы часто называют динамическими массивами :

int n = 42;
double a[n][5]; // ошибка
auto p1 = new  double[n][5];  // OK
auto p2 = new  double[5][n];  // ошибка: только первое измерение может быть неконстантным
auto p3 = new (double[n][5]); // ошибка: синтаксис (1) не может использоваться для динамических массивов

Поведение не определено, если значение в первом измерении (преобразованное в целочисленный тип или тип перечисления при необходимости) отрицательно.

(until C++11)

В следующих случаях значение выражения, задающего первое измерение, является недопустимым:

  • выражение имеет неклассовый тип и его значение до преобразования в std::size_t отрицательно;
  • выражение имеет классовый тип и его значение после пользовательской функции преобразования и до второго стандартного преобразования отрицательно;
  • значение выражения больше некоторого определяемого реализацией предела;
  • значение меньше количества элементов массива, предоставленных в списке инициализации в фигурных скобках (включая завершающий ' \0 ' для строкового литерала ).

Если значение в первом измерении недопустимо по любой из этих причин,

  • если после преобразования в std::size_t первое измерение является основным константным выражением и оно потенциально вычисляется , программа является некорректной,
  • в противном случае, если функция выделения памяти, которая была бы вызвана, является небросающей (включая перегрузки std::nothrow , не объявленные noexcept ), выражение new возвращает нулевой указатель требуемого типа результата,
  • в противном случае выражение new не вызывает функцию выделения памяти и вместо этого выбрасывает исключение типа, который соответствовал бы обработчику типа std::bad_array_new_length .
(since C++11)

Первое измерение нулевого размера допустимо, и функция выделения памяти вызывается.

Если new-initializer является списком инициализации в фигурных скобках, и первое измерение потенциально вычисляется и не является core constant expression , проверяются семантические ограничения copy-initializing гипотетического элемента массива из пустого списка инициализации.

(since C++11)

Аллокация

Выражение new выделяет память, вызывая соответствующую функцию выделения . Если type является не-массивным типом, имя функции — operator new . Если type является массивным типом, имя функции — operator new [ ] .

Как описано в функции выделения памяти , программа на C++ может предоставлять глобальные и специфичные для класса замены этих функций. Если new выражение начинается с опционального оператора :: , как в :: new T или :: new T [ n ] , специфичные для класса замены будут проигнорированы (функция ищется в глобальной области видимости ). В противном случае, если T является типом класса, поиск начинается в области видимости класса T .

При вызове функции выделения памяти выражение new передает количество запрашиваемых байтов в качестве первого аргумента типа std::size_t , которое точно равно sizeof ( T ) для не-массивных типов T .

Выделение памяти для массивов может предусматривать неопределённые накладные расходы, которые могут различаться от одного вызова new к другому, если только выбранная функция выделения не является стандартной невыделяющей формой. Указатель, возвращаемый выражением new , будет смещён на это значение относительно указателя, возвращённого функцией выделения. Многие реализации используют накладные расходы массива для хранения количества объектов в массиве, которое используется выражением delete [ ] для вызова правильного количества деструкторов. Кроме того, если выражение new используется для выделения массива char , unsigned char , или std::byte (начиная с C++17) , оно может запросить дополнительную память у функции выделения, если это необходимо для гарантии корректного выравнивания объектов всех типов, не превышающих размер запрашиваемого массива, если они впоследствии размещаются в выделенном массиве.

new выражения могут опускать или объединять выделения памяти, выполняемые через заменяемые функции выделения. В случае опускания, память может быть предоставлена компилятором без вызова функции выделения (это также позволяет оптимизировать неиспользуемые new выражения). В случае объединения, выделение памяти выражением new E1 может быть расширено для предоставления дополнительной памяти другому выражению new E2 , если выполняются все следующие условия:

1) Время жизни объекта, выделенного E1 , строго содержит время жизни объекта, выделенного E2 .
2) E1 и E2 вызывали бы одну и ту же заменяемую глобальную функцию выделения.
3) Для выбрасывающей функции выделения, исключения в E1 и E2 были бы сначала перехвачены одним и тем же обработчиком.

Заметим, что эта оптимизация разрешена только для выражений new , а не для любых других методов вызова заменяемой функции выделения: delete [ ] new int [ 10 ] ; может быть оптимизировано, но operator delete ( operator new ( 10 ) ) ; не может.

(since C++14)

При вычислении константного выражения , вызов функции выделения всегда опускается. Только выражения new , которые в противном случае привели бы к вызову заменяемой глобальной функции выделения, могут быть вычислены в константных выражениях.

(since C++20)

Размещающий new

Если предоставлены placement-args , они передаются функции выделения памяти в качестве дополнительных аргументов. Такие функции выделения памяти известны как "placement new ", по аналогии со стандартной функцией выделения void * operator new ( std:: size_t , void * ) , которая просто возвращает свой второй аргумент без изменений. Это используется для конструирования объектов в выделенной памяти:

// в любой блочной области видимости...
{
    // Статически выделяем память с автоматической длительностью хранения
    // достаточного размера для любого объекта типа "T".
    alignas(T) unsigned char buf[sizeof(T)];
    T* tptr = new(buf) T; // Создаем объект "T", размещая его непосредственно в 
                          // заранее выделенной памяти по адресу "buf".
    tptr->~T();           // Вы должны **вручную** вызвать деструктор объекта,
                          // если программа зависит от его побочных эффектов.
}                         // Выход из этой блочной области автоматически освобождает "buf".

Примечание: данная функциональность инкапсулирована функциями-членами классов Allocator .

При выделении памяти для объекта, требование выравнивания которого превышает __STDCPP_DEFAULT_NEW_ALIGNMENT__ или для массива таких объектов, выражение new передает требование выравнивания (обернутое в std::align_val_t ) в качестве второго аргумента функции выделения (для форм размещения, placement-arg появляются после выравнивания, в качестве третьего, четвертого и т.д. аргументов). Если разрешение перегрузки завершается неудачей (что происходит, когда класс-специфичная функция выделения определена с другой сигнатурой, поскольку она скрывает глобальные), разрешение перегрузки предпринимается второй раз, без выравнивания в списке аргументов. Это позволяет класс-специфичным функциям выделения, не учитывающим выравнивание, иметь приоритет над глобальными функциями выделения, учитывающими выравнивание.

(начиная с C++17)
new T;      // вызывает operator new(sizeof(T))
            // (C++17) или operator new(sizeof(T), std::align_val_t(alignof(T))))
new T[5];   // вызывает operator new[](sizeof(T)*5 + overhead)
            // (C++17) или operator new(sizeof(T)*5+overhead, std::align_val_t(alignof(T))))
new(2,f) T; // вызывает operator new(sizeof(T), 2, f)
            // (C++17) или operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)

Если функция выделения памяти, не генерирующая исключений (например, выбранная с помощью new ( std:: nothrow ) T ), возвращает нулевой указатель из-за ошибки выделения памяти, то выражение new возвращается немедленно и не пытается инициализировать объект или вызвать функцию освобождения памяти. Если нулевой указатель передается в качестве аргумента в неразмещающее выражение placement new , что приводит к тому, что выбранная стандартная неразмещающая функция выделения памяти возвращает нулевой указатель, поведение не определено.

Инициализация

Объект, созданный выражением new , инициализируется в соответствии со следующими правилами.

Если type не является типом массива, единственный объект конструируется в полученной области памяти:

  • Если new-initializer отсутствует, объект default-initialized .
  • Если new-initializer представляет собой список выражений в круглых скобках, объект direct-initialized .
  • Если new-initializer является заключенным в фигурные скобки списком инициализации, объект list-initialized .
(since C++11)

Если type является типом массива, инициализируется массив объектов:

  • Даже если первое измерение равно нулю, семантические ограничения для инициализации по умолчанию гипотетического элемента всё равно должны быть соблюдены.
  • Даже если первое измерение равно нулю, семантические ограничения инициализации значением гипотетического элемента всё равно должны быть соблюдены.
(начиная с C++11)
(начиная с C++20)

Сбой инициализации

Если инициализация завершается выбрасыванием исключения (например, из конструктора), программа находит подходящую функцию освобождения, затем:

  • Если подходящая функция освобождения памяти может быть найдена, она вызывается для освобождения памяти, в которой создавался объект. После этого исключение продолжает распространяться в контексте выражения new .
  • Если однозначно соответствующая функция освобождения не может быть найдена, распространение исключения не приводит к освобождению памяти объекта. Это допустимо только если вызванная функция выделения памяти не выделяет память, в противном случае это, вероятно, приведет к утечке памяти.

Область действия поиска соответствующей функции освобождения определяется следующим образом:

  • Если выражение new не начинается с :: , и выделяемый тип является либо типом класса T , либо массивом типа класса T , выполняется поиск имени функции освобождения в области видимости класса T .
  • В противном случае, или если ничего не найдено в области видимости класса T , имя функции освобождения ищется путем поиска в глобальной области видимости .

Для функции неразмещающего выделения памяти используется обычный поиск функции освобождения для нахождения соответствующей функции освобождения (см. delete-expression ).

Для функции размещающего выделения соответствующая функция освобождения должна иметь то же количество параметров, и каждый тип параметра, кроме первого, идентичен соответствующему типу параметра функции выделения (после преобразований параметров ).

  • Если поиск находит единственную подходящую функцию освобождения памяти, эта функция будет вызвана; в противном случае функция освобождения памяти вызвана не будет.
  • Если поиск находит непомещающую функцию освобождения памяти и эта функция, рассматриваемая как помещающая функция освобождения памяти, была бы выбрана как соответствие для функции выделения памяти, программа является некорректной.

В любом случае, соответствующая функция освобождения (если есть) должна быть неделированной и (since C++11) доступной из точки, где появляется выражение new .

struct S
{
    // Функция размещающего выделения памяти:
    static void* operator new(std::size_t, std::size_t);
    // Функция неразмещающего освобождения памяти:
    static void operator delete(void*, std::size_t);
};
S* p = new (0) S; // ошибка: функция неразмещающего освобождения памяти соответствует
                  //        функции размещающего выделения памяти

Если функция освобождения вызывается в new выражении (из-за сбоя инициализации), аргументы, передаваемые этой функции, определяются следующим образом:

  • Первый аргумент — это значение (типа void * ), возвращённое вызовом функции выделения памяти.
  • Остальные аргументы (только для функций размещающего освобождения) — это placement-args , переданные в функцию размещающего выделения памяти.

Если реализации разрешено вводить временный объект или создавать копию любого аргумента в рамках вызова функции выделения, не определено, используется ли один и тот же объект при вызове как функций выделения, так и освобождения памяти.

Утечки памяти

Объекты, созданные выражениями new (объекты с динамической продолжительностью хранения), существуют до тех пор, пока указатель, возвращённый выражением new , не будет использован в соответствующем delete-expression . Если исходное значение указателя утрачивается, объект становится недостижимым и не может быть освобождён: происходит утечка памяти .

Это может произойти, если указателю присвоено:

int* p = new int(7); // динамически выделенный int со значением 7
p = nullptr; // утечка памяти

или если указатель выходит из области видимости:

void f()
{
    int* p = new int(7);
} // утечка памяти

или из-за исключения:

void f()
{
    int* p = new int(7);
    g();      // может выбросить исключение
    delete p; // корректно, если исключения нет
} // утечка памяти, если g() выбрасывает исключение

Для упрощения управления динамически выделяемыми объектами результат выражения new часто сохраняется в умном указателе : std::auto_ptr (до C++17) std::unique_ptr , или std::shared_ptr (начиная с C++11) . Эти указатели гарантируют, что выражение delete выполняется в ситуациях, показанных выше.

Примечания

Itanium C++ ABI требует, чтобы накладные расходы на выделение массива были нулевыми, если тип элементов создаваемого массива является тривиально разрушаемым. Аналогично поступает MSVC.

Некоторые реализации (например, MSVC до версии VS 2019 v16.7) требуют ненулевых накладных расходов на выделение массива при использовании невыделяющего размещающего new для массивов, если тип элементов не является тривиально разрушаемым, что больше не соответствует стандарту после CWG issue 2382 .

Нераспределяющее размещающее выражение new для массива, которое создает массив unsigned char , или std::byte (начиная с C++17) , может быть использовано для неявного создания объектов в заданной области памяти: оно завершает время жизни объектов, перекрывающихся с массивом, а затем неявно создает объекты типов с неявным временем жизни в массиве.

std::vector предоставляет аналогичную функциональность для одномерных динамических массивов.

Ключевые слова

new

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 74 C++98 значение в первом измерении должно иметь целочисленный тип разрешены перечислимые типы
CWG 299 C++98 значение в первом измерении должно
иметь целочисленный или перечислимый тип
разрешены классовые типы с единственной
функцией преобразования в целочисленный
или перечислимый тип
CWG 624 C++98 поведение было неопределенным, когда
размер выделяемого объекта превышал
ограничение, определенное реализацией
память не выделяется и в этом случае
выбрасывается исключение
CWG 1748 C++98 размещающий non-allocating placement new должен
проверять, является ли аргумент нулевым
неопределенное поведение для нулевого аргумента
CWG 1992 C++11 new ( std:: nothrow ) int [ N ]
мог выбрасывать std::bad_array_new_length
изменено на возврат нулевого указателя
CWG 2102 C++98 было неясно, должно ли инициализация по умолчанию/значением
быть корректной при инициализации пустых массивов
требуется
CWG 2382 C++98 размещающий non-allocating placement массивный new
мог требовать накладных расходов на выделение
такие накладные расходы на выделение запрещены
CWG 2392 C++11 программа могла быть некорректной, даже если
первое измерение не является потенциально-вычисляемым
корректна в этом случае
P1009R2 C++11 граница массива не могла быть
выведена в выражении new
вывод разрешен

Смотрите также