Удаление буфера с помощью другого типа указателя?

Скажем, у меня есть следующий С++:

char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;

Является ли это безопасным в соответствии со стандартом С++? Нужно ли возвращаться к char*, а затем использовать delete[]? Я знаю, что это будет работать в большинстве компиляторов С++, потому что это обычные обычные данные без деструкторов. Гарантировано ли это безопасно?

Ответ 1

Не гарантируется безопасность. Здесь соответствующая ссылка в С++ FAQ lite:

[16.13] Можно ли удалить [] при удалении массива какого-либо встроенного типа (char, int и т.д.)?

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.13

Ответ 2

Нет, это поведение undefined - компилятор может правдоподобно сделать что-то другое, а в качестве записи С++ FAQ, которая thudbang связана с сообщением, operator delete[] может быть перегружен, чтобы сделать что-то другое с operator delete. Вы можете иногда уйти от него, но также хорошей практикой является привычка сопоставлять delete [] с новым [] для случаев, когда вы не можете.

Ответ 3

Я очень сомневаюсь в этом.

Существует много сомнительных способов освобождения памяти, например, вы можете использовать delete в своем массиве char (а не delete[]), и это, вероятно, будет работать нормально. я в блоге подробно об этом (извинения за саморегуляцию, но это проще, чем переписывание всего).

Компилятор - это не столько проблема, как платформа. Большинство библиотек будут использовать методы распределения базовой операционной системы, что означает, что один и тот же код может вести себя по-разному на Mac против Windows против Linux. Я видел примеры этого, и каждый из них был сомнительным кодом.

Самый безопасный подход - всегда выделять и освобождать память с использованием одного и того же типа данных. Если вы выделяете char и возвращаете их в другой код, вам может быть лучше предоставить определенные методы allocate/deallocate:

SOME_STRUCT* Allocate()
{
    size_t cb; // Initialised to something
    return (SOME_STRUCT*)(new char[cb]);
}

 

void Free(SOME_STRUCT* obj)
{
    delete[] (char*)obj;
}

(Перегрузка операторов new и delete также может быть вариантом, но мне никогда не нравилось это делать.)

Ответ 4

Стандарт С++ [5.3.5.2] объявляет:

Если операнд имеет тип класса, операнд преобразуется в тип указателя, вызывая вышеупомянутое преобразование функция, а преобразованный операнд используется вместо исходного операнда для остальной части этого раздела. В любом альтернативой, значение операнда удаления может быть значением нулевого указателя. Если это не значение нулевого указателя, в первом альтернативный (удалить объект), значение операнда удаления должно быть указателем на объект без массива или указателем на подобъектом (1.8), представляющим базовый класс такого объекта (раздел 10). Если нет, то поведение undefined. В секунду альтернативный (удалить массив), значение операнда удаления должно быть значением указателя, которое было результатом предыдущего array new-expression.77) Если нет, то поведение undefined. [Примечание: это означает, что синтаксис выражения-удаления должен соответствовать типу объекта, выделенного новым, а не синтаксис нового выражения. -end note] [Примечание: указатель к типу const может быть операндом удаления-выражения; нет необходимости отбрасывать константу (5.2.11) выражение указателя перед его использованием в качестве операнда выражения-удаления. -end note]

Ответ 5

Это очень похожий вопрос на тот, на который я ответил: текст ссылки

Короче говоря, нет, он небезопасен в соответствии со стандартом С++. Если по какой-то причине вам нужен объект SOME_STRUCT, выделенный в области памяти с разницей в размере от size_of(SOME_STRUCT) (и лучше было бы больше!), Тогда вам лучше использовать функцию выделения, такую ​​как глобальная operator new выполнить выделение, а затем создать экземпляр объекта в необработанной памяти с помощью места размещения new. Размещение new будет чрезвычайно дешевым, если тип объекта не имеет конструктора.

void* p = ::operator new( cb );
SOME_STRUCT* pSS = new (p) SOME_STRUCT;

// ...

delete pSS;

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

::operator new и ::operator delete являются ближайшими к С++ эквивалентами malloc и free, и поскольку они (в отсутствие переопределений классов) называются соответствующими выражениями new и delete, они могут (с уход!) можно использовать в комбинации.

Ответ 6

Пока это работает, я не думаю, что вы можете гарантировать его безопасность, потому что SOME_STRUCT не является char * (если только это не просто typedef).

Кроме того, поскольку вы используете разные типы ссылок, если вы продолжаете использовать доступ * p, а память была удалена, вы получите ошибку времени выполнения.

Ответ 7

Это будет работать нормально, если указана указатель на и указатель, на который вы указываете, оба являются POD. В этом случае ни один деструктор не будет вызван в любом случае, и распределитель памяти не знает или не заботится о типе, хранящемся в памяти.

Единственный случай, когда это нормально с не-POD-типами, - это то, что pointee является подтипом указателя (например, вы указываете на автомобиль с транспортным средством *), а деструктор указателя объявлен виртуальным.

Ответ 8

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

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

Ответ 9

Я изменил код, чтобы использовать malloc/free. В то время как я знаю, как MSVC реализует новый /delete для простых-старых данных (и SOME_STRUCT в этом случае была структурой Win32, поэтому простой C), я просто хотел знать, была ли она переносной техникой.

Это не так, поэтому я буду использовать что-то, что есть.

Ответ 10

Если вы используете malloc/free вместо new/delete, malloc и free не будут заботиться о типе.

Итак, если вы используете C-подобный POD (простые старые данные, такие как встроенный тип или структура), вы можете malloc какого-то типа и освободить другого. обратите внимание, что это плохой стиль, даже если он работает.