удалить буфер через указатель другого типа? - PullRequest
10 голосов
/ 16 сентября 2008

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

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

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

Ответы [ 10 ]

9 голосов
/ 16 сентября 2008

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

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

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

6 голосов
/ 16 сентября 2008

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

4 голосов
/ 16 сентября 2008

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

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

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

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

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

2 голосов
/ 20 сентября 2008

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

Короче говоря, нет, это небезопасно в соответствии со стандартом 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 не имеет пользовательского оператора delete. Эта техника также устраняет необходимость в любых приведениях.

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

2 голосов
/ 16 сентября 2008

C ++ Standard [5.3.5.2] объявляет:

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

0 голосов
/ 17 сентября 2008

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

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

0 голосов
/ 16 сентября 2008

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

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

0 голосов
/ 16 сентября 2008

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

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

0 голосов
/ 16 сентября 2008

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

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

0 голосов
/ 16 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...