Могу ли я новый [], затем нарисуйте указатель, а затем удалите [] безопасно со встроенными типами на С++?
В моем коде у меня есть фактически следующее:
wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );
Типы, о которых идет речь, являются встроенными и поэтому имеют тривиальные деструкторы. В VС++ код выше работает полностью - new[]
просто выделяет память, тогда delete[]
просто освобождает его.
Это приемлемо в С++? Это поведение undefined?
Ответы
Ответ 1
Моя первоначальная мысль заключалась в том, что это поведение undefined.
5.3.5/3: "Во втором альтернативе (удалить массив), если динамический тип объекта, подлежащего удалению отличается от его статического типа, поведение undefined. 73).
Сноска 73 гласит: "Это означает, что объект нельзя удалить с помощью указателя типа void*
, потому что нет объектов типа void
".
Возможно, объект в вашем примере не имеет динамического типа, поскольку в определении "динамический тип" в 1.3.3 упоминается "самый производный объект", а определение "самого производного объекта" в 1.8/4 говоря об объектах типа класса. Поэтому я продолжал смотреть:
5.2.10/3: "[reinterpret_cast] может или не может создать представление отличается от исходного значения
5.3.5/2:" Значением операнда delete
должно быть значение указателя которые возникли из предыдущего массива <Я > новое выражение".
Я не уверен, приведет ли reinterpret_cast к тому же значению указателя, которое было введено, или нет. Возможно, это прояснилось каким-то другим стандартом, которого я еще не нашел. Я бы не назвал этот код "ОК", не найдя что-то, чтобы окончательно указать, что если вы reinterpret_cast указатель, результатом будет то же самое "значение указателя", как и раньше, так что, передав его для удаления [], вы передаете "значение указателя" из нового [].
5.2.10/7: "За исключением того, что кастинг [между определенными типами указателей] и обратно к своему первоначальному типу дает исходное значение указателя, результат такое преобразование указателя не определенно".
Для меня это выглядит как плохая новость - он явно не говорит, что приведение дает ту же ценность, только то, что пара отливок сверху и назад дает одинаковое значение. Это говорит мне о том, что одиночному актеру разрешено давать другое значение, но оно только наводящее на размышления, а не явное. Это обычная проблема с правилом: "Если в стандарте не указано поведение, то поведение undefined". Просто потому, что он не указывает его в любом из параграфов, который я могу найти с помощью индекса, не означает, что он не указывает его где-то еще...
Мы знаем, что на практике мы можем отличать вещи от unsigned char *, чтобы проверить их байты или void *, чтобы скопировать POD с помощью memcpy, поэтому для создания псевдонимов должно быть какое-то преимущество. Вы можете подумать, что если ваша реализация действительно создает псевдонимы с определенными отбрасываниями, то вы передаете "то же значение", которое вы получили от нового []. Но я все еще не уверен, что это достаточно хорошо для удаления []. Я думаю, что мне не хватает чего-то важного.
Ответ 2
Это поведение undefined, потому что delete[]
вызывает неправильный деструктор. Однако wchar_t
и char
являются POD, поэтому у них нет выделенного деструктора, и все delete[]
это вызывает реализацию кучи, чтобы освободить указатель. Поэтому он, скорее всего, будет работать, байт не будет потерян. Но, строго говоря, это все еще undefined.
Ответ 3
По крайней мере, поскольку я прочитал его, у вас есть статический тип (тип указателя), который отличается от динамического типа (реальный тип объекта, на который он указывает). В этом случае применяется второе предложение §5.3.5/3:
Во втором альтернативе (удалить массив), если динамический тип объект, подлежащий удалению, отличается от его статического типа, поведение undefined.
Изменить: поскольку вы, по-видимому, хотите выделить буфер "сырой" памяти вместо массива объектов, я бы посоветовал использовать ::operator new
вместо new[]
. В этом случае то, что вы делаете, четко определено, а также дает читателю четкое указание на намерение.
Ответ 4
iso14882 раздел 5.2.10.3:
The mapping performed by reinterpret_cast is is implementation defined
iso14882 раздел 5.3.5.2:
The value of the operand of delete[] shall be the pointer value which resulted from a previous array new-expression
Другими словами, в реализации реализовано, действительно ли delete [] вызывает поведение undefined. Уберите четкость.
Ответ 5
Так как wchar_t
и char
являются встроенными типами, будет вызвана правильная функция освобождения (void operator delete(void* ptr)
), и для вызова не будет деструктора.
Однако в стандарте С++ 03 результат reinterpret_cast<T1*>(T2*)
равен undefined (раздел 5.2.10.7):
Указатель на объект может быть явно преобразован в указатель на объект другого типа. Кроме этого преобразование rvalue типа "указатель на T1" в тип "указатель на T2" (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не более строгие, чем требования T1) и обратно к его первоначальному типу дает исходное значение указателя, результат такого преобразования указателя не указан.
Из практического POV я не могу представить себе реализацию, где значение wchar_t*
не является допустимым значением char*
, поэтому ваш код должен быть в порядке на всех платформах. Просто не стандартно...
Ответ 6
Оператор delete [] внутренне использует цикл некоторой формы для уничтожения элементов вашего массива. Если элементы являются разными объектами, будет использоваться другой деструктор, который может вызвать поведение undefined. Поскольку wchar и char - примитивные типы - это, вероятно, не вызовет никакого нежелательного поведения.
ПРЕДУПРЕЖДЕНИЕ: ЕСЛИ ВЫ ПРОДОЛЖИТЕ ЧТЕНИЕ, ТАК НА ВАШЕМ СОБСТВЕННОМ ПЕРИЛЕ!! ОСНОВНЫЕ ОПИСАНИЯ undefined ПОВЕДЕНИЕ ВПЕРЕД. ЭТО ТОЛЬКО ДЛЯ ОБРАЗОВАТЕЛЬНЫХ ЦЕЛЕЙ.
Пример 1:
Если у вас было два объекта, размер которых был одинаковым, и все их деструкторы сделали нулевой объем памяти, то опять-таки это, вероятно, не вызовет недопустимого поведения.
Пример 2:
Однако, если у вас было два объекта, в которых один тип инкапсулировал один 4-байтовый дескриптор ресурса, а другой имел два таких элемента, и вы выбрали массив из более позднего в особый случай - ну тогда вы пропустили бы половину ручками вашего массива. Ситуация будет выглядеть следующим образом:
.. 2: [1 | 2] [1 | 2] бесплатно..
где "2:" представляет размер массива. После downcast компилятор сгенерирует удаление, которое воспринимает данные так:
.. 2: [1] [1] бесплатно...
поэтому после того, как свободные вещи будут выглядеть так:
.. FREE [1 | 2] бесплатно..
Ответ 7
Почему вы используете reinterpret_cast <.. > ? Если вы пишете что-то в чистом С++, вам не нужно переводить ретрансляцию. В вашем случае вы не выделяете память для объекта. Вы выделяете память для wchar_t. Почему бы не использовать для строки вместо массива wchar_t?