Скажем, у меня есть следующий C++:
char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;
Это безопасно по стандарту C++? Нужно ли мне выполнять обратную трансляцию на char*
, а затем использовать delete[]
? Я знаю, что это будет работать в большинстве компиляторов C++, потому что это обычные обычные данные без деструкторов. Гарантированно ли это безопасно?
Нет, это неопределенное поведение - компилятор вполне может сделать что-то другое, и, как говорится в записи часто задаваемых вопросов C++, на которую ссылается удар, operator delete[]
может быть перегружен, чтобы делать что-то отличное от operator delete
. Иногда это может сойти с рук, но также полезно выработать привычку сопоставлять delete [] с new [] в тех случаях, когда вы не можете.
Это не гарантирует безопасность. Вот соответствующая ссылка в C++ FAQ lite:
[16.13] Могу ли я сбросить []
при удалении массива какого-либо встроенного типа (char
, int
и т. д.)?
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.13
Хотя этот должен работает, я не думаю, что вы можете гарантировать его безопасность, потому что SOME_STRUCT не является char * (если это просто typedef).
Кроме того, поскольку вы используете разные типы ссылок, если вы продолжите использовать доступ * p, а память была удалена, вы получите ошибку времени выполнения.
Стандарт C++ [5.3.5.2] заявляет:
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In either alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a subobject (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression.77) If not, the behavior is undefined. [ Note: this means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression. —end note ] [ Note: a pointer to a const type can be the operand of a delete-expression; it is not necessary to cast away the constness (5.2.11) of the pointer expression before it is used as the operand of the delete-expression. —end note ]
Я очень в этом сомневаюсь.
Существует множество сомнительных способов освобождения памяти, например, вы можете использовать delete
в своем массиве char
(а не delete[]
), и он, скорее всего, будет работать нормально. Я блоггинг подробно об этом (извиняюсь за самостоятельную ссылку, но это проще, чем все это переписывать).
Проблема не столько в компиляторе, сколько в платформе. Большинство библиотек будут использовать методы распределения базовой операционной системы, что означает, что один и тот же код может вести себя по-разному на Mac, в Windows и в Linux. Я видел примеры этого, и каждый из них был сомнительным кодом.
Самый безопасный подход - всегда выделять и освобождать память с использованием одного и того же типа данных. Если вы выделяете char
и возвращаете их в другой код, вам может быть лучше предоставить определенные методы выделения / освобождения:
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
также может быть вариантом, но мне никогда не нравилось это делать.)
Это будет работать нормально, если память, указывающая на и, указатель, которым вы указываете, является POD. В этом случае деструктор в любом случае не будет вызываться, а распределитель памяти не знает и не заботится о типе, хранящемся в памяти.
Единственный случай, когда это нормально для типов, отличных от POD, - это если указатель является подтипом указателя (например, вы указываете на автомобиль с транспортным средством *), а деструктор указателя был объявлен виртуальным.
Это небезопасно, и до сих пор никто из ответов не подчеркивал безумие этого. Просто не делайте этого, если вы считаете себя настоящим программистом или когда-нибудь хотите работать профессиональным программистом в команде. Вы можете только сказать, что ваша структура содержит недеструктор В данный момент, однако вы закладываете неприятную, возможно, специфичную для компилятора и системы ловушку на будущее. Кроме того, ваш код вряд ли будет работать должным образом. Лучшее, на что вы можете надеяться, - это то, что он не выйдет из строя. Однако я подозреваю, что вы постепенно получите утечку памяти, так как при распределении массивов через new очень часто дополнительная память в байтах прежний выделяется для возвращаемого указателя. Вы не освободите память, о которой думаете. Хорошая процедура распределения памяти должна устранить это несоответствие, как и такие инструменты, как Lint и т. д.
Просто не делайте этого и вычистите из своего разума все мыслительные процессы, которые заставили вас даже подумать о такой ерунде.
Я изменил код, чтобы использовать malloc / free. Хотя я знаю, как MSVC реализует new / delete для простых-старых данных (и SOME_STRUCT в этом случае была структурой Win32, такой простой C), я просто хотел знать, был ли это переносной метод.
Это не так, поэтому я воспользуюсь тем, что есть.
Если вы используете malloc / free вместо new / delete, malloc и free не будут заботиться о типе.
Поэтому, если вы используете POD в стиле C (простые старые данные, такие как встроенный тип или структура), вы можете выделить один тип и освободить другой. обратите внимание, что это плохой стиль, даже если он работает.
Это очень похож на тот, на который я ответил здесь: текст ссылки
Короче, нет, по стандарту C++ это небезопасно. Если по какой-то причине вам нужен объект 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
не имеет настраиваемого оператора delete. Эта техника также устраняет необходимость в каких-либо гипсовых повязках.
::operator new
и ::operator delete
являются ближайшим эквивалентом C++ для malloc
и free
, и, поскольку они (при отсутствии переопределения классов) вызываются соответственно выражениями new
и delete
, их можно (осторожно!) Использовать в комбинации.