Размещение массива-new требует неопределенных накладных расходов в буфере?

5.3.4 [expr.new] проекта C ++ 11 февраля приведен пример:

new(2,f) T[5] приводит к вызову operator new[](sizeof(T)*5+y,2,f) .

Здесь x и y – неотрицательные неопределенные значения, представляющие издержки распределения массива; результат нового выражения будет компенсирован этой суммой из значения, возвращаемого operator new[] . Эти служебные данные могут применяться во всех новых выражениях массива, включая те, которые ссылаются на функцию функции библиотеки operator new[](std::size_t, void*) и другие функции распределения мест. Количество накладных расходов может варьироваться от одного вызова нового к другому. -End пример ]

Теперь возьмите следующий пример кода:

 void* buffer = malloc(sizeof(std::string) * 10); std::string* p = ::new (buffer) std::string[10]; 

Согласно вышеприведенной цитате вторая строка new (buffer) std::string[10] будет внутренне вызывать operator new[](sizeof(std::string) * 10 + y, buffer) (перед построением отдельного std::string объекты). Проблема в том, что если y > 0 , предварительно выделенный буфер будет слишком мал!

Итак, как я узнаю, сколько памяти нужно предварительно выделить при использовании размещения массива-new?

 void* buffer = malloc(sizeof(std::string) * 10 + how_much_additional_space); std::string* p = ::new (buffer) std::string[10]; 

Или же стандарт где-то гарантирует, что y == 0 в этом случае? Опять же, цитата говорит:

Эти служебные данные могут применяться во всех новых выражениях массива, включая те, которые ссылаются на функцию функции библиотеки operator new[](std::size_t, void*) и другие функции распределения мест.

Не используйте operator new[](std::size_t, void* p) если вы не знаете априорный ответ на этот вопрос. Ответ – это деталь реализации и может измениться с помощью компилятора / платформы. Хотя он обычно стабилен для любой данной платформы. Например, это то, что указано Itanium ABI .

Если вы не знаете ответа на этот вопрос, напишите свой собственный массив размещения new, который может проверить это во время выполнения:

 inline void* operator new[](std::size_t n, void* p, std::size_t limit) { if (n <= limit) std::cout << "life is good\n"; else throw std::bad_alloc(); return p; } int main() { alignas(std::string) char buffer[100]; std::string* p = new(buffer, sizeof(buffer)) std::string[3]; } 

Изменяя размер массива и проверяя n в приведенном выше примере, вы можете сделать вывод о своей платформе. Для моей платформы y - 1 слово. Sizeof (word) изменяется в зависимости от того, компилирую ли я для 32-битной или 64-битной архитектуры.

Обновление: после некоторого обсуждения я понимаю, что мой ответ больше не применяется к вопросу. Я оставлю его здесь, но реальный ответ определенно по-прежнему требует.

Я буду рад поддержать этот вопрос с некоторой щедростью, если хороший ответ не будет найден в ближайшее время.

Я остановлюсь на этом вопросе, насколько я понимаю, надеясь, что более короткая версия может помочь другим понять, что задают. Вопрос в том:

Всегда ли правильная конструкция? Есть arr == addr в конце?

 void * addr = std::malloc(N * sizeof(T)); T * arr = ::new (addr) T[N]; // #1 

Мы знаем из стандарта, что # 1 вызывает вызов ::operator new[](???, addr) , где ??? – это неуказанное число, не меньшее N * sizeof(T) , и мы также знаем, что этот вызов возвращает только addr и не имеет других эффектов. Мы также знаем, что arr смещается от addr соответственно. То, что мы не знаем, является ли объем памяти, на который указывает addr , достаточно большой или как мы знаем, сколько памяти выделяется.


Вы, кажется, путаете несколько вещей:

  1. Ваш пример вызывает operator new[]() , а не operator new() .

  2. Функции распределения ничего не создают. Они выделяют .

Случается, что выражение T * p = new T[10]; причины:

  1. вызов operator new[]() с аргументом размера 10 * sizeof(T) + x ,

  2. десять вызовов конструктора по умолчанию T , эффективно ::new (p + i) T() .

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


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


Обновление. Как случайный fragment информации, вы должны заметить, что глобальные функции размещения – новые не требуются. То есть, когда вы строите объект или массив на месте, например:

 T * p = ::new (buf1) T; T * arr = ::new (buf10) T[10]; 

Затем соответствующие вызовы ::operator new(std::size_t, void*) и ::operator new[](std::size_t, void*) ничего не делают, кроме как возвращают их второй аргумент. Однако вы не знаете, на что должен buf10 : он должен указывать на 10 * sizeof(T) + y байтов памяти, но вы не можете знать y .

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

Вы можете утверждать, что это означает, что вам также необходимо вручную уничтожить объекты в вашей арене памяти. Однако вызов delete[] array на указателе, возвращенном из new места размещения, не будет работать: он будет использовать версию operator delete[] () без места размещения operator delete[] () ! То есть, при использовании new места размещения вам необходимо вручную уничтожить объект (ы) и освободить память.

Как упоминал Kerrek SB в комментариях, этот дефект был впервые представлен в 2004 году , и он был решен в 2012 году как:

КСР согласилась с тем, что EWG является подходящим местом для решения этой проблемы.

Затем дефект был сообщен EWG в 2013 году , но закрыт как NAD (предположительно означает «Not A Defect») с комментарием:

Проблема заключается в попытке использовать массив new для размещения массива в уже существующем хранилище. Нам не нужно использовать для этого новый массив; просто постройте их.

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


Следствием, не упомянутым в другом месте в streamе, является то, что этот код вызывает неопределенное поведение для всех T :

 T *ptr = new T[N]; ::operator delete[](ptr); 

Даже если мы соблюдаем правила жизни (т. Е. T либо имеет тривиальное разрушение, либо программа не зависит от побочных эффектов деструктора), проблема в том, что ptr был скорректирован для этого неуказанного файла cookie, поэтому это неправильное значение для перейдите к operator delete[] .

После прочтения соответствующих стандартных разделов я считаю, что размещение новых типов массивов – это просто бесполезная идея, и единственная причина, по которой это допускается стандартом, – это общий способ описания нового оператора:

Новое выражение пытается создать объект typeid (8.1) или newtypeid, к которому он применяется. Тип этого объекта – это назначенный тип. Этот тип должен быть полным типом объекта, но не абстрактным типом classа или его массивом (1.8, 3.9, 10.4). [Примечание: поскольку ссылки не являются объектами, ссылки не могут быть созданы с помощью новых выражений. ] [Примечание: typeid может быть cvqualified type, и в этом случае объект, созданный новым выражением, имеет тип cvqualified. ]

 new-expression: ::(opt) new new-placement(opt) new-type-id new-initializer(opt) ::(opt) new new-placement(opt) ( type-id ) new-initializer(opt) new-placement: ( expression-list ) newtypeid: type-specifier-seq new-declarator(opt) new-declarator: ptr-operator new-declarator(opt) direct-new-declarator direct-new-declarator: [ expression ] direct-new-declarator [ constant-expression ] new-initializer: ( expression-list(opt) ) 

Для меня кажется, что array placement new просто связано с компактностью определения (все возможные использования в качестве одной схемы), и, похоже, нет веских оснований для его запрета.

Это оставляет нас в ситуации, когда у нас есть бесполезный оператор, которому требуется память, прежде чем будет известно, сколько из этого потребуется. Единственные решения, которые я вижу, – либо объединить память, либо надеяться, что компилятор не захочет больше, чем предоставил, или перераспределит память в переопределенном array placement new функции / метода (что скорее поражает цель использования array placement new в первую очередь ).


Чтобы ответить на вопрос, заданный Kerrek SB: Ваш пример:

 void * addr = std::malloc(N * sizeof(T)); T * arr = ::new (addr) T[N]; // #1 

не всегда правильно. В большинстве реализаций arr!=addr (и для этого есть веские причины), поэтому ваш код недействителен, и ваш буфер будет переполнен.

Об этих «хороших причинах» – обратите внимание, что вы выпускаете стандартные создатели из некоторого домохозяйства при использовании array new оператора-оператора, а array placement new в этом отношении не отличается. Обратите внимание, что вам не нужно сообщать delete[] о длине массива, поэтому эта информация должна храниться в самом массиве. Где? Именно в этой дополнительной памяти. Без него delete[] ‘ing потребует сохранения длины массива отдельно (поскольку stl делает использование циклов и не-размещения new )

Эти служебные данные могут применяться во всех новых выражениях массива, включая те, которые ссылаются на функцию функции библиотеки operator new[](std::size_t, void*) и другие функции распределения мест.

Это дефект в стандарте. Ходят слухи, что они не смогли найти волонтера, чтобы написать ему исключение (Сообщение № 1165).

Не заменяемое размещение массива-new не может использоваться с выражениями delete[] , поэтому вам нужно пройти через массив и вызвать каждый деструктор .

Накладные расходы нацелены на определяемые пользователем размещения в массиве – новые функции, которые выделяют память так же, как обычный T* tp = new T[length] . Они совместимы с delete[] , следовательно, накладные расходы, которые переносят длину массива.

  • расположение каталога данных mysql
  • Как получить COM-сервер для Excel, написанный на VB.NET, установленный и зарегистрированный в списке серверов автоматизации?
  • Ошибка: невозможно найти adb в SDK в Android Studio
  • Как я могу получить последнюю версию JRE / JDK как zip-файл, а не EXE или MSI-установщик?
  • Что такое полный class базы данных Android для существующей базы данных SQLite?
  • Создание настройки приложения в visual studio 2013
  • Установите Visual Studio 2013 на Windows 7
  • Установка VB6 на Windows 7 (или Windows 8) (или Windows 10)
  • Добавление пользовательских prerequsites в проект настройки визуальной студии
  • Как найти UpgradeCode и ProductCode установленного приложения в Windows 7
  • Как регистрировать типы файлов / расширения с помощью установщика WiX?
  • Interesting Posts

    Из Word в PDF, включая закладки

    Как проверить, загружен ли плагин jQuery?

    Угловой 2: Как стиль элемента хоста компонента?

    Почему Java ограничивает размер метода до 65535 байт?

    Могу ли я настроить загрузку Windows без остановки проверки пароля даже при установке пароля?

    C #: Разница между ‘+ = anEvent’ и ‘+ = new EventHandler (anEvent)’

    Как я должен придерживаться этического подхода к хранилищу паролей пользователей для последующего поиска в открытом виде?

    Как доказать, что провайдер блокирует входящий трафик на некоторых портах?

    Аутентификация на основе токена в веб-API без какого-либо пользовательского интерфейса

    Как я могу отобразить URL-адрес в качестве изображения в ячейке excel?

    Объедините неравные данные и замените отсутствующие строки на 0

    Лучший способ проверить, существует ли объект в Entity Framework?

    Wake on LAN перестала работать после установки Windows 7

    Строковый URL-адрес в RouteValueDictionary

    Откройте новое окно подсказки / терминала из Java

    Давайте будем гением компьютера.