Пространства имён
Варианты
Действия

Выражение new

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений(до C++17*)
Спецификатор noexcept(C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype(C++11)
auto(C++11)
alignas(C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr(C++11)
Определяемые пользователем(C++11)
Утилиты
Атрибуты(C++11)
Types
Объявление typedef
Объявление псевдонима типа(C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Выражение new
Классы
Свойства функции класса
Специальные функции-элементы
Шаблоны
Разное
 
Выражения
Общие
Категории значений (lvalue, rvalue, xvalue)
Порядок оценки (точки последовательности)
Константные выражения
Потенциально оцениваемые выражения
Первичные выражения
Лямбда-выражения(C++11)
Литералы
Целочисленные литералы
Литералы с плавающей запятой
Логические литералы
Символьные литералы, включая управляющие последовательности
Строковые литералы
Литерал нулевого указателя(C++11)
Пользовательский литерал(C++11)
Операторы
a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
++a, --a, a++, a--
+a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
a||b, a&&b, !a
a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(начиная с C++20)
a[b], *a, &a, a->b, a.b, a->*b, a.*b
a(...), a,b, a?b:c
выражение new
выражение delete
выражение throw
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
Выражения свёртки(C++17)
Альтернативные представления операторов
Приоритет и ассоциативность
Перегрузка операторов
Сравнение по умолчанию(C++20)
Преобразования
Неявные преобразования
Обычные арифметические преобразования
const_cast
static_cast
reinterpret_cast
dynamic_cast
Явные преобразования: (T)a, T(a), auto(a), auto{a}(начиная с C++23)
Пользовательское преобразование
 

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

Содержание

[править]Синтаксис

::(необязательно)new(тип)инициализатор (необязательно) (1)
::(необязательно)newnew-типинициализатор (необязательно) (2)
::(необязательно)new(параметры-размещения)(тип)инициализатор (необязательно) (3)
::(необязательно)new(параметры-размещения)new-типинициализатор (необязательно) (4)
1) Пытается создать объект типа, обозначенного идентификатором-типатип, который может быть типом массива, и может включать спецификатор типа заполнителя(начиная с C++11), или включать имя шаблона класса, аргумент которого должен быть выведен с помощью вывода аргументов шаблона класса(начиная с C++17).
2) То же, что (1), но new-тип не может включать круглые скобки:
new int(*[10])();// ошибка: анализируется как (new int) (*[10]) () new (int(*[10])());// правильно: выделяет массив из 10 указателей на функции
Вдобавок new-тип жадный: он будет включать каждый токен, который может быть частью декларатора:
new int+1;// правильно: анализируется как (new int) + 1,// инкрементирует указатель, возвращаемый new int new int*1;// ошибка: анализируется как (new int*) (1)
3) То же, что (1), но предоставляет дополнительные аргументы для функции распределения, смотрите размещающий new.
4) То же, что (2), но предоставляет дополнительные аргументы функции распределения.


инициализатор не является обязательным, если

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

[править]Объяснение

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

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

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

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

(до C++11)

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

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

(начиная с C++11)

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

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

[править]Распределение

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

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

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

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

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

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

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

(начиная с C++14)

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

(начиная с C++20)
[править]Размещающий new

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

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

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

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

(начиная с 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 возвращается немедленно, оно не пытается инициализировать объект или вызвать функцию освобождения. Если нулевой указатель передаётся в качестве аргумента не выделяющему память выражению new, что заставляет выбранную стандартную функцию выделения памяти без выделения памяти возвращать нулевой указатель, поведение не определено.

[править]Конструирование

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

  • Для типа не-массива, в полученной области памяти создаётся единственный объект.
(начиная с C++11)
  • Если тип или new-тип является типом массива, инициализируется массив объектов.
(начиная с C++11)
(начиная с C++20)

Если инициализация завершается выдачей исключения (например, из конструктора), если выражение new выделило какое-либо хранилище, оно вызывает соответствующую функцию освобождения: operator delete для типа не массива, operator delete[] для типа массива. Функция освобождения ищется в глобальной области видимости, если выражение new использовало синтаксис ::new, в противном случае она ищется в области видимости T, если T это тип класса. Если неудачная функция распределения была обычной (без размещения), поиск функции освобождения выполняется согласно правилам, описанным в выражении delete. Для неудачного размещающего new, все типы параметров, кроме первого, соответствующей функции освобождения должны быть идентичны параметрам размещающего new. При вызове функции освобождения памяти используется значение, полученное ранее из функции распределения, переданное в качестве первого аргумента, выравнивание передаётся как необязательный аргумент выравнивания(начиная с C++17), и параметры-размещения, если есть, передаются в качестве дополнительных аргументов размещения. Если функция освобождения не найдена, память не освобождается.

[править]Утечки памяти

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

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

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 выполняется в ситуациях, показанных выше.

[править]Ключевые слова

new

[править]Примечание

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

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

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

[править]Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

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

[править]Смотрите также

close