Могу я новый [], затем бросать указатель, затем удалять [] безопасно со встроенными типами в C ++?

В моем кодексе у меня есть эффективно следующее:

wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );

Рассматриваемые типы все встроены и таким образом, у них есть тривиальные деструкторы. В VC ++ кодекс выше работ хорошо - new[] просто ассигнует память, тогда delete[] просто освобождает его.

Действительно ли это приемлемо в C ++? Это - неопределенное поведение?

17
задан sharptooth 26 January 2010 в 15:20
поделиться

7 ответов

Сначала я подумал, что это неопределенное поведение.

5.3.5 / 3: «Во втором варианте ( массив удаления ), если динамический тип удаляемого объекта отличается от его статического типа, поведение не определено. 73) .

Сноска 73 гласит: «Это означает, что объект не может быть удален с помощью указателя типа void * , потому что там не являются объектами типа void ".

Возможно, объект в вашем примере не имеет динамического типа, поскольку в определении« динамического типа »в 1.3.3 упоминается «наиболее производный объект» и определение «наиболее производный объект» в 1.8 / 4 говорят об объектах типа класса, поэтому я продолжал искать:

5.2.10/3: "[reinterpret_cast] может, а может и не создать представление , отличное от исходного значения"

5.3.5 / 2: "Значение операнда delete должно быть значением указателя , полученным из предыдущего массива new-expression ".

Я не уверен, приводит ли reinterpret_cast к тому же значению указателя, что и было введено, или нет. Возможно, это прояснено каким-то другим стандартом, которого я еще не нашел. Я бы не назвал этот код «ОК», не найдя чего-либо, чтобы окончательно заявить, что если вы повторно интерпретируете_каст указателя, результатом будет то же «значение указателя», что и раньше, так что, передав его в delete [], вы передаете «значение указателя "из нового [].

5.2.10 / 7: "За исключением того, что приведение [между определенными типами указателей] и обратно к исходному типу дает исходное значение указателя, результат такого преобразование указателя не указано ".

Для меня это выглядит как плохая новость - здесь явно не говорится, что приведение дает одно и то же значение, а только то, что пара повторений дает одно и то же значение. Это наводит на мысль, что одно приведение может давать другое значение, но это только наводит на размышления, а не явно. Это обычная проблема с правилом, что «если в стандарте не указано поведение, то поведение не определено». Тот факт, что он не заявляет об этом ни в одном из абзацев, которые я могу найти с помощью индекса, не означает, что он не указывает это где-то еще ...

Мы знаем, что на практике мы можем преобразовывать объекты в unsigned char *, чтобы проверять их байты, или в void *, чтобы копировать POD с помощью memcpy, поэтому должны быть некоторые преобразования, гарантированно создающие псевдонимы. Вы можете подумать, что если ваша реализация действительно создает псевдонимы с определенными приведениями,затем вы передаете "то же значение", которое получили от new []. Но я все еще не уверен, что этого достаточно для удаления []. Я думаю, что упускаю что-то важное.

18
ответ дан 30 November 2019 в 11:44
поделиться

Это неопределенное поведение, потому что delete[] вызывает неверный деструктор. Однако wchar_t и char являются POD, поэтому у них нет выделенного деструктора, и все, что делает delete[], это вызывает реализацию кучи, чтобы освободить указатель. Следовательно, скорее всего, это сработает, ни один байт не будет потерян. Но, строго говоря, он все равно не определен.

9
ответ дан 30 November 2019 в 11:44
поделиться

С WHAR_T и CHAR являются как встроенные типы, правильная функция дефилакации ( оператор пустоты удаляет (void * ptr) ) И нет деструктора, чтобы позвонить.

Однако стандарт C ++ 03 говорит, что результат ReinterPret_Cast (T2 *) не определен (раздел 5.2.10.7):

указатель на объект может быть явно преобразован в указатель на объект другого типа. Кроме этого Преобразование указателя «rvalue типа» на T1 «на тип» указатель на T2 "(где T1 и T2 - типы объектов и где требования к выравниванию T2 не являются более строгими, чем у T1) и обратно к его первоначальному типу дает первоначальное значение указателя, результат такого преобразования указателя не указан.

От практического POV я не могу представить внедрение, где значение wchar_t * не является действительным значением * значение, поэтому ваш код должен быть ОК на всех платформах. Просто не стандартный совместимый ...

1
ответ дан 30 November 2019 в 11:44
поделиться

ISO14882 Section 5.2.10.3:

Thete Mapping, выполняемый ReinterPret_Cast, является определяется реализацией

Секция ISO14882 5.3.5.2:

В. Значение операнда удаления [] должно быть значение, которое привело к Другими словами, предыдущий массив нового выражения

, его реализация определяется независимо от того, вызывает ли удаление [] undefined поведением. Держаться подальше.

4
ответ дан 30 November 2019 в 11:44
поделиться

Оператор удаления [] внутренне использует петлю некоторой формы для разрушения элементов вашего массива. Если элементы разные объекты будут использоваться другой деструктор - что потенциально может вызвать неопределенное поведение. Поскольку является WHAR и CHAR - примитивные типы - это, вероятно, не вызывает нежелательного поведения.

Предупреждение: если вы продолжите читать, сделайте это на свой страх! Валовые описания неопределенного поведения впереди. Это только для образовательных целей.

Пример 1:

Если у вас были два объекта, которые были одинаковыми размерами, и все их деструктор делали, было расти в памяти, то снова это, вероятно, не приведет к неразрушающему поведению.

Пример 2:

Однако, если у вас были два объекта, когда один тип инкапсулировал одну 4 байтовую ручку для ресурса, а другой имел два таких элемента, и вы бросили массив позже в сингулярное дело - ну тогда Вы бы утесили половину ручек вашего массива. Ситуация будет выглядеть следующим образом:

.. 2: [1 | 2] [1 | 2] бесплатно ..

где «2:» представляет собой размер массива. После уска. Компилятор будет генерировать удаление, которая воспринимала данные как так:

.. 2: [1] [1] бесплатно ...

Поэтому после того, как свободные вещи будут выглядеть так:

. . Бесплатно [1 | 2] бесплатно ..

0
ответ дан 30 November 2019 в 11:44
поделиться

Первоначально я думал, что это неопределенное поведение.

5,3,5/3: "Во второй альтернативе ( удалить массив ), если динамический тип удаляемого объекта отличается от своего статического типа, поведение не определено. 73).

Сноска 73 гласит: «Это означает, что объект не может быть удален с помощью указателя типа void * , поскольку отсутствуют объекты типа void ».

Возможно, объект в примере не имеет динамического типа, поскольку в определении «динамического типа» в 1,3,3 упоминается «самый производный объект», а в определении «самого производного объекта» в 1,8/4 говорится об объектах типа класса. Поэтому я продолжал смотреть:

5,2,10/3: "[переосмыслить _ привести] может, или не может, произвести представление отличается от исходного значения «

5,3,5/2:» Значение операнда исключить должно быть значением указателя который является результатом предыдущего массива new-expression ".

Я не уверен, приводит ли reinterpret_cast к тому же значению указателя, что и было введено, или нет. Возможно, он очищен каким-то другим битом стандарта, который я еще не нашел. Я бы не назвал этот код «OK», не найдя что-то, чтобы окончательно заявить, что если вы reinterpret_cast указатель, результат будет таким же «значение указателя», как раньше, так что, передавая его для удаления [] вы передаете «значение указателя» из нового [].

5,2,10/7: "За исключением литья [между определенными типами указателей] и обратно к своему исходному типу дает исходное значение указателя, результат такое преобразование указателя не указан. "

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

Мы знаем, что на практике мы можем поместить вещи в неподписанный символ *, чтобы проверить их байты, или аннулировать * для копирования POD с помощью memcpy, поэтому должны быть некоторые слепки, гарантированные для создания псевдонимов. Вы можете подумать, что если ваша реализация действительно создает псевдонимы с определенными слепками, то вы передаете «то же значение», которое вы получили от нового []. Но я все еще не уверен, что это достаточно хорошо для удаления []. Думаю, я упускаю что-то важное.

-121--2313363-

Поскольку вы все равно были в Википедии: Введите безопасность .

Тип безопасности означает, грубо говоря, что язык запрещает вам случайно смешивать ваши типы.

memcpy не является типом-безопасно, поскольку можно легко скопировать память некоторых int в массив char и получить бессмысленные данные. printf не безопасен, так как можно предоставить спецификатор формата % i с последовательностью; опять же, последовательность будет интерпретироваться как int , и вы закончите с мусором. (В некоторых ситуациях компилятор VC++ проверяет строку формата .)

std:: vector < T > является безымянным, поскольку позволяет вводить в нее только значения данного типа T . (Конечно, вы можете делать явные типы, но точка в том, что вы должны быть явным о том, что делать что-то, что не является типом безопасности).

-121--2129031-

По крайней мере, как я бы его прочитал, у вас есть статический тип (тип указателя), который отличается от динамического типа (реальный тип объекта, на который он указывает). При этом применяется второе предложение § 5.3.5/3:

Во второй альтернативе (удалить массив), если динамический тип удаляемый объект отличается от его статического типа, поведение не определено.

Edit: Поскольку вы, очевидно, хотите выделить буфер «сырой» памяти вместо массива объектов, я бы посоветовал использовать :: оператор new вместо new [] . В этом случае то, что вы делаете, четко определено, а также дает читателю четкое указание на намерение.

5
ответ дан 30 November 2019 в 11:44
поделиться

Почему вы используете reinterpret_cast <..>? Если вы пишете что-то на чистом C ++, вам не нужно переинтерпретировать приведение. В вашем случае вы не выделяете память для объекта. Вы выделяете память для wchar_t. Почему бы не использовать строку вместо массива wchar_t?

0
ответ дан 30 November 2019 в 11:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: