удаление буфера через указатель другого типа?

StackOverflow https://stackoverflow.com/questions/70880

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

Самый безопасный подход — всегда выделять и освобождать память, используя один и тот же тип данных.Если вы выделяете 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 Object) значение операнда удаления должно быть указателем на объект без ареста или указатель на субобект (1.8), представляющий базовый класс такого объект (пункт 10).В противном случае поведение не определено.Во второй альтернативе (массив удаления) значение операнда удаления должно быть значением указателя, которое возникло из предыдущего массива новой экспрессии.77) Если нет, то поведение не определен.[ Примечание:Это означает, что синтаксис удаления-экспрессии должен соответствовать типу объекта, выделенного новым, а не синтаксисом новой экспрессии.—конечная заметка] [ Примечание:Указатель на тип конната может быть операндом удаленной экспрессии;Нет необходимости отбрасывать констанцию ​​(5.2.11) выражения указателя, прежде чем оно будет использовано в качестве операнда удаления-экспрессии.—конечная заметка]

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

Короче говоря, нет, это небезопасно по стандарту C++.Если по какой-то причине вам нужен объект SOME_STRUCT, расположенный в области памяти, размер которой отличается от size_of(SOME_STRUCT) (и лучше, чтобы оно было больше!), тогда вам лучше использовать функцию необработанного распределения, например global 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 являются ближайшим эквивалентом C++ malloc и free и поскольку они (при отсутствии переопределений классов) вызываются соответствующим образом new и delete выражения, которые их можно (с осторожностью!) использовать в сочетании.

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top