8 Expressions [expr]

8.3 Unary expressions [expr.unary]

8.3.4 New [expr.new]

Эти new-expressionпопытки создать объект из type-idили new-type-idк которому она применяется. Тип этого объекта -allocated type. Этот тип должен быть полный тип объекта, но не абстрактный тип класса или их массив ([intro.object],[basic.types],[class.abstract]). [ Note: Поскольку ссылки не являются объектами, ссылки не могут быть созданы с помощью new-expressions. ] [ Может быть резюме квалифицированных типа, в этом случае объект , созданный имеет резюме квалифицированных типа. ]end noteNote: type-idnew-expressionend note

new-expression:
	::opt new new-placementopt new-type-id new-initializeropt 
	::opt new new-placementopt ( type-id ) new-initializeropt

new-placement:
	( expression-list )
new-type-id:
	type-specifier-seq new-declaratoropt
new-declarator:
	ptr-operator new-declaratoropt 
	noptr-new-declarator
noptr-new-declarator:
	[ expression ] attribute-specifier-seqopt
	noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
new-initializer:
	( expression-listopt )
	braced-init-list

Объекты , созданные new-expressionимеют dynamic storage duration. [ Время существования такой сущности не обязательно ограничивается областью, в которой она создана. ] Если сущность не является объектом массива, возвращает указатель на созданный объект. Если это массив, возвращает указатель на начальный элемент массива.Note: end notenew-expressionnew-expression

Если aplaceholder type появляется в type-specifier-seqa new-type-idили type-ida new-expression, выделенный тип выводится следующим образом: Пусть init будет the new-initializer, если есть, и T будет the new-type-idor type-idof the new-expression, тогда выделенный тип - это тип, выведенный для переменнойx в изобретенном объявлении ([dcl.spec.auto]) :

T x init ;

[Example:

new auto(1);                    // allocated type is int
auto x = new auto('a');         // allocated type is char, x is of type char*

template<class T> struct A { A(T, T); };
auto y = new A{1, 2};           // allocated type is A<int>

end example]

В new-type-ida new-expression- самая длинная возможная последовательность new-declarators. [ Note: Это предотвращает неоднозначность между описателем операторами&,&&, *и[] и их экспрессиями аналогами. ] [end noteExample:

new int * i;                    // syntax error: parsed as (new int*) i, not as (new int)*i

Это* декларатор указателя, а не оператор умножения. ]end example

[ Скобки в из может иметь неожиданные последствия. [Note: new-type-idnew-expressionExample:

new int(*[10])();               // error

неправильно сформирован, потому что привязка

(new int) (*[10])();            // error

Вместо этого явно заключенная в скобки версияnew оператора может использоваться для создания объектовcompound types:

new (int (*[10])());

выделяет массив10 указателей на функции (без аргументов и с возвратомint). ] ]end exampleend note

Когда выделенный объект является массивом (то есть используется noptr-new-declaratorсинтаксис или new-type-idили type-idобозначает тип массива), new-expressionвыдает указатель на начальный элемент (если есть) массива. [ Note: Обаnew int иnew int[10] имеют тип,int* а типnew int[i][10] - это ] В a принадлежит к соответствующему типу массива.int (*)[10] end noteattribute-specifier-seqnoptr-new-declarator

Каждый constant-expressionэлемент a noptr-new-declaratorдолжен бытьconverted constant expression типа a std​::​size_­t и иметь строго положительное значение. Объект expressionв a noptr-new-declaratorнеявно преобразуется вstd​::​size_­t. [ Example: Принимая во внимание определениеint n = 42, new float[n][5] правильно сформирован (потому чтоn это expressionв А noptr-new-declarator), но new float[5][n] плохо сформированные (потому чтоn это не выражение постоянной). ]end example

В expressiona noptr-new-declaratorошибочно, если:

  • выражение неклассового типа и его значение перед преобразованием в std​::​size_­t меньше нуля;

  • выражение относится к классу и его значение до применения второго стандартного преобразования ([over.ics.user])79 меньше нуля;

  • его значение таково, что размер выделенного объекта будет превышать размер, определенный реализациейlimit; или

  • new-initializerявляется braced-init-listи количество элементов массива , для которых предусмотрены инициализаторов (включая завершающий'\0' вstring literal) превышает количество элементов , чтобы инициализировать.

Если expressionпосле преобразования вstd​::​size_­t:

  • если expressionявляется основным постоянным выражением, программа имеет неправильный формат;

  • в противном случае функция распределения не вызывается; вместо

    • если функция выделения, которая должна была быть вызвана, имеет спецификацию исключения, не вызывающего выброса ([except.spec]), значением new-expression является значение нулевого указателя требуемого типа результата;

    • в противном случае new-expressionзавершается выдача исключения типа, который соответствует типу handler ([except.handle]) std​::​bad_­array_­new_­length.

Когда значение expressionравно нулю, вызывается функция выделения для выделения массива без элементов.

A new-expressionможет получить хранилище для объекта, вызвав функцию распределения ([basic.stc.dynamic.allocation]). Если new-expressionзавершается выдачей исключения, он может освободить хранилище, вызвав adeallocation function. Если выделенный тип не является массивом, имя функции выделения - это, а имя функции освобождения - . Если выделенный тип является типом массива, имя функции распределения - это, а имя функции освобождения - . [ Реализация должна дать определения по умолчанию для глобальных функций распределения ( , , ). Программа на C ++ может предоставлять альтернативные определения этих функций ( ) и / или версий для конкретных классов ( ). Набор функций выделения и освобождения, который может быть вызван a, может включать в себя функции, которые не выполняют выделение или освобождение; например, см . ]operator new operator deleteoperator new[]operator delete[]Note: [basic.stc.dynamic][new.delete.single][new.delete.array][replacement.functions][class.free]new-expression[new.delete.placement]end note

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

Реализации разрешено опускать вызов заменяемой функции глобального распределения ([new.delete.single],[new.delete.array]). Когда это происходит, хранилище вместо этого предоставляется реализацией или предоставляется путем расширения выделения другой new-expression. Реализация может расширить выделение a, new-expressione1 чтобы предоставить хранилище для a, new-expressione2 если бы было верно следующее, если бы выделение не было расширено:

  • оценкаe1 выполняется до оценки e2, и

  • e2 оценивается всякий раз, когдаe1 получает память, и

  • обаe1 иe2 вызывают одну и ту же заменяемую функцию глобального распределения, и

  • если функция распределения, вызваннаяe1 иe2 выбрасывает, любые исключения, возникшие при оценке любого изe1 или e2 , будут сначала перехвачены в том же обработчике, и

  • значения указателя, созданныеe1 иe2 являются операндами для оценки delete-expressions, и

  • оценкаe2 выполняется перед оценкой того, delete-expressionчей операнд является значением указателя, созданнымe1.

[Example:

  void mergeable(int x) {
    // These allocations are safe for merging:
    std::unique_ptr<char[]> a{new (std::nothrow) char[8]};
    std::unique_ptr<char[]> b{new (std::nothrow) char[8]};
    std::unique_ptr<char[]> c{new (std::nothrow) char[x]};

    g(a.get(), b.get(), c.get());
  }

  void unmergeable(int x) {
    std::unique_ptr<char[]> a{new char[8]};
    try {
      // Merging this allocation would change its catch handler.
      std::unique_ptr<char[]> b{new char[x]};
    } catch (const std::bad_alloc& e) {
      std::cerr << "Allocation failed: " << e.what() << std::endl;
      throw;
    }
  }

end example]

Когда a new-expressionвызывает функцию выделения и это выделение не было расширено, он new-expressionпередает запрошенный объем пространства в функцию выделения в качестве первого аргумента типа std​::​size_­t. Этот аргумент должен быть не меньше размера создаваемого объекта; он может быть больше размера создаваемого объекта, только если объект является массивом. Для массивов char,unsigned charиstd​::​byte, разность между результатом new-expressionи адресом , возвращаемым функцией распределения должна быть целым кратной строжайшим фундаментальным alignment requirement любым типа объекта, размер которого не больше , чем размер массива создается. [ Поскольку предполагается, что функции распределения возвращают указатели на хранилище, которое соответствующим образом выровнено для объектов любого типа с фундаментальным выравниванием, это ограничение накладных расходов на выделение массива допускает общую идиому выделения массивов символов, в которые позже будут помещены объекты других типов. ]Note: end note

Когда a new-expressionвызывает функцию выделения и это выделение было расширено, аргумент размера для вызова выделения не должен быть больше, чем сумма размеров для пропущенных вызовов, как указано выше, плюс размер для расширенного вызова, если бы он не был расширен. , плюс любые отступы, необходимые для выравнивания выделенных объектов в выделенной памяти.

new-placementСинтаксис используется для предоставления дополнительных аргументов в функцию распределения; такое выражение называется aplacement new-expression.

Разрешение перегрузки выполняется при вызове функции, созданной путем сборки списка аргументов. Первый аргумент - это объем запрошенного пространства и имеет типstd​::​size_­t. Если тип выделенного объекта имеет выравнивание по новому-расширенному, следующий аргумент - это выравнивание типа и имеет типstd​::​align_­val_­t. Если используется new-placementсинтаксис, initializer-clauses в нем expression-list находятся следующие аргументы. Если совпадающая функция не найдена и у выделенного типа объекта есть выравнивание "новое-расширенное", аргумент выравнивания удаляется из списка аргументов, и снова выполняется разрешение перегрузки.

[Example:

  • new T приводит к одному из следующих вызовов:

    operator new(sizeof(T))
    operator new(sizeof(T), std::align_val_t(alignof(T)))
  • new(2,f) T приводит к одному из следующих вызовов:

    operator new(sizeof(T), 2, f)
    operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
  • new T[5] приводит к одному из следующих вызовов:

    operator new[](sizeof(T) * 5 + x)
    operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)))
  • new(2,f) T[5] приводит к одному из следующих вызовов:

    operator new[](sizeof(T) * 5 + x, 2, f)
    operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)), 2, f)

Здесь каждый экземплярx - неотрицательное неопределенное значение, представляющее накладные расходы на выделение массива; результат new-expressionбудет компенсирован на эту сумму от значения, возвращаемогоoperator new[]. Эти накладные расходы могут применяться ко всем массивам new-expressions, включая те, которые относятся к библиотечной функцииoperator new[](std​::​size_­t, void*) и другим функциям размещения. Сумма накладных расходов может варьироваться от одного вызоваnew к другому. ]end example

[ Note: Если функция распределения не имеет не-выброса exception specification, это указывает на сбой выделения памяти путем выдачи исключения ( , пункт , ); в противном случае он возвращает ненулевой указатель. Если функция выделения имеет спецификацию исключения, не вызывающего выброса, она возвращает значение null, чтобы указать на сбой выделения памяти, и ненулевой указатель в противном случае. ] Если функция распределения - это form ( ) без выделения памяти, которая возвращает значение null, поведение не определено. В противном случае, если функция распределения возвращает ноль, инициализация не должна выполняться, функция освобождения не должна вызываться, а значение должно быть нулевым.std​::​bad_­alloc [basic.stc.dynamic.allocation][except][bad.alloc]end note[new.delete.placement]new-expression

[ Note: Когда функция распределения возвращает значение, отличное от нуля, это должен быть указатель на блок памяти, в котором было зарезервировано пространство для объекта. Предполагается, что блок памяти правильно выровнен и имеет запрошенный размер. Адрес созданного объекта не обязательно будет таким же, как у блока, если объект является массивом. ]end note

A, new-expressionкоторый создает объект типа,T инициализирует этот объект следующим образом:

  • Если new-initializerопущен, объект инициализируется по умолчанию ([dcl.init]). [ Note: Если инициализация не выполняется, объект имеет неопределенное значение. ]end note

  • В противном случае new-initializerинтерпретируется в соответствии с правилами инициализации[dcl.init] для прямой инициализации.

Вызов функции распределения выполняется до вычислений выражений в new-initializer. Инициализация выделенного объекта выполняется перед вычислением значения new-expression.

Если new-expressionсоздает объект или массив объектов типа класса, контроль доступа и неоднозначности выполняется для функции распределения,deallocation functionа такжеconstructor. Если new-expression создает массив объектов типа класса,destructor потенциально вызывается.

Если какая-либо часть описанной выше инициализации объекта80 завершается выдачей исключения и может быть найдена подходящая функция освобождения, вызывается функция освобождения, чтобы освободить память, в которой создавался объект, после чего исключение продолжает распространяться в контексте. из new-expression. Если не удается найти однозначно совпадающую функцию освобождения памяти, распространение исключения не приводит к освобождению памяти объекта. [ Note: Это уместно, когда вызываемая функция распределения не выделяет память; в противном случае это может привести к утечке памяти. ]end note

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

Объявление функции освобождения размещения соответствует объявлению функции распределения размещения, если она имеет такое же количество параметров и после преобразования параметров ([dcl.fct]) все типы параметров, кроме первого, идентичны. Если поиск находит единственную подходящую функцию освобождения, будет вызвана эта функция; в противном случае функция освобождения памяти не будет вызвана. Если поиск находит ausual deallocation function с параметром типаstd​::​size_­t и эта функция, рассматриваемая как функция освобождения размещения, была бы выбрана в качестве соответствия для функции распределения, программа имеет неправильный формат. Для функции распределения, не связанной с размещением, обычный поиск функции освобождения используется для поиска соответствующей функции освобождения ([expr.delete]) [Example:

struct S {
  // Placement allocation function:
  static void* operator new(std::size_t, std::size_t);

  // Usual (non-placement) deallocation function:
  static void operator delete(void*, std::size_t);
};

S* p = new (0) S;   // ill-formed: non-placement deallocation function matches
                    // placement allocation function

end example]

Если a new-expressionвызывает функцию освобождения, она передает значение, возвращенное при вызове функции распределения, в качестве первого аргумента типаvoid*. Если вызывается функция освобождения размещения, ей передаются те же дополнительные аргументы, которые были переданы функции распределения размещения, то есть те же аргументы, что и те, которые указаны в new-placementсинтаксисе. Если реализации разрешено делать копию любого аргумента как часть вызова функции распределения, разрешается делать копию (того же исходного значения) как часть вызова функции освобождения или повторно использовать копию сделано как часть вызова функции распределения. Если копия пропущена в одном месте, нет необходимости опускать ее в другом.

Если функция преобразования возвращает целочисленный тип со знаком, второе стандартное преобразование преобразуется в тип без знакаstd​::​size_­t и, таким образом, предотвращает любую попытку впоследствии обнаружить отрицательное значение.

Это может включать оценку new-initializerи / или вызов конструктора.