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

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

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

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

10 ответов


это не гарантировано, чтобы быть безопасным. Вот соответствующая ссылка в C++ FAQ lite:

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

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


нет, это неопределенное поведение-компилятор может правдоподобно сделать что-то другое, и как запись c++ FAQ, что thudbang связаны с Говорит:operator delete[] может быть перегружен, чтобы сделать что-нибудь другое operator delete. Иногда это может сойти вам с рук, но это также хорошая практика, чтобы привыкнуть сопоставлять delete[] с new[] Для случаев, когда вы не можете.


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

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

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

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

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 операторы также могут быть вариант, но мне никогда не нравилось это делать.)


стандарт C++ [5.3.5.2] заявляет:

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


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

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

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

// ...

delete pSS;

это будет работать большую часть времени. Он всегда должен работать, если SOME_STRUCT является ПОДСТРУКТУРОЙ. Он также будет работать в других случаях, если SOME_STRUCTконструктор не бросает и если SOME_STRUCT не имеет пользовательского оператора delete. Эта техника также устраняет необходимость в любых слепках.

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


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

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


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

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


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

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


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

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


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

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