Безопасно ли удалять указатель на пустоту?

Предположим, у меня есть следующий код:

void* my_alloc (size_t size) { return new char [size]; } void my_free (void* ptr) { delete [] ptr; } 

Это безопасно? Или нужно, чтобы ptr был ptr char* до удаления?

Это зависит от «безопасного». Это обычно работает, потому что информация хранится вместе с указателем о самом распределении, поэтому деллалокатор может вернуть его в нужное место. В этом смысле он «безопасен», пока ваш распределитель использует внутренние метки границ. (Многие делают.)

Однако, как упоминалось выше, удаление указателя void не будет вызывать деструкторы, что может быть проблемой. В этом смысле это не «безопасно».

Нет никаких оснований делать то, что вы делаете, как вы это делаете. Если вы хотите написать свои собственные функции удаления, вы можете использовать шаблоны функций для генерации функций с правильным типом. Хорошей причиной для этого является создание распределителей пулов, которые могут быть чрезвычайно эффективными для определенных типов.

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

Удаление с помощью указателя void не определено стандартом C ++ – см. Раздел 5.3.5 / 3:

В первом альтернативе (объект удаления), если статический тип операнда отличается от его динамического типа, статический тип должен быть базовым classом динамического типа операнда, а статический тип должен иметь виртуальный деструктор или поведение не определено , Во втором альтернативе (удалить массив), если динамический тип подлежащего удалению объекта отличается от его статического типа, поведение не определено.

И его сноска:

Это означает, что объект не может быть удален с помощью указателя типа void *, потому что нет объектов типа void

,

Это не очень хорошая идея, а не то, что вы сделали бы на C ++. Вы теряете информацию о типе без причины.

Ваш деструктор не будет вызван на объекты в вашем массиве, которые вы удаляете, когда вы вызываете его для не примитивных типов.

Вместо этого вы должны заменить new / delete.

Удаление void *, вероятно, освободит вашу память правильно, но это неправильно, потому что результаты не определены.

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

Удаление указателя void опасно, поскольку деструкторы не будут вызывать значение, на которое оно указывает. Это может привести к утечке памяти / ресурсов в вашем приложении.

Поскольку char не имеет специальной логики деструктора. ЭТО не будет работать.

 class foo { ~foo() { printf("huzza"); } } main() { foo * myFoo = new foo(); delete ((void*)foo); } 

Д’тор не будет вызван.

Если вы хотите использовать void *, почему бы вам не использовать только malloc / free? new / delete – это больше, чем просто управление памятью. В принципе, new / delete вызывает конструктор / деструктор, и происходит больше вещей. Если вы просто используете встроенные типы (например, char *) и удаляете их через void *, это будет работать, но все же это не рекомендуется. В нижней строке используется malloc / free, если вы хотите использовать void *. В противном случае вы можете использовать функции шаблонов для вашего удобства.

 template T* my_alloc (size_t size) { return new T [size]; } template void my_free (T* ptr) { delete [] ptr; } int main(void) { char* pChar = my_alloc(10); my_free(pChar); } 

Если вы действительно должны это сделать, почему бы не вырезать среднего человека ( new и delete операторов) и вызвать глобальный operator new и operator delete напрямую? (Конечно, если вы пытаетесь задействовать new и delete операторы, вам действительно нужно переопределить operator new и operator delete .)

 void* my_alloc (size_t size) { return ::operator new(size); } void my_free (void* ptr) { ::operator delete(ptr); } 

Обратите внимание: в отличие от malloc() operator new бросает std::bad_alloc при сбое (или вызывает new_handler если он зарегистрирован).

Вопрос не имеет смысла. Ваша путаница может быть частично вызвана неаккуратными языками, которые люди часто используют с delete :

Вы используете delete для уничтожения объекта, который был динамически распределен. Сделайте это, вы создадите выражение удаления с указателем на этот объект . Вы никогда не удаляете указатель. То, что вы действительно делаете, это «удалить объект, который идентифицируется по его адресу».

Теперь мы видим, почему вопрос не имеет смысла: указатель void не является «адресом объекта». Это просто адрес, без какой-либо семантики. Возможно, это произошло из адреса фактического объекта, но эта информация потеряна, потому что она была закодирована в типе исходного указателя. Единственный способ восстановить указатель объекта – вернуть указатель void обратно в указатель объекта (что требует от автора знать, что означает указатель). void сам является неполным типом и, следовательно, никогда не является типом объекта, а указатель void никогда не может использоваться для идентификации объекта. (Объекты идентифицируются совместно по их типу и их адресу.)

Многие люди уже прокомментировали, что нет, небезопасно удалять указатель на пустоту. Я согласен с этим, но я также хотел добавить, что если вы работаете с указателями void, чтобы выделять непрерывные массивы или что-то подобное, вы можете сделать это с помощью new чтобы вы могли безопасно использовать delete (с , гм, немного дополнительной работы). Это делается путем выделения указателя void области памяти (называемого «ареной»), а затем подачи указателя на арену на новый. См. Этот раздел в FAQ по C ++ . Это общий подход к реализации пулов памяти на C ++.

Это вряд ли повод для этого.

Прежде всего, если вы не знаете тип данных, и все, что вам известно, это то, что оно void* , тогда вы действительно должны просто обрабатывать эти данные как беспринципную капли двоичных данных ( unsigned char* ) и использовать malloc / free чтобы справиться с этим. Это требуется иногда для таких вещей, как данные формы волны и т. П., Где вам нужно передать указатели void* на C apis. Хорошо.

Если вы знаете тип данных (т. Е. У него есть ctor / dtor), но по какой-то причине вы получили указатель void* (по какой бы то ни было причине), вы действительно должны вернуть его к типу, который вам известен это будет , и вызовите delete на нем.

Я использовал void * (ака неизвестные типы) в своей структуре, хотя в отражении кода и других трюках двусмысленности, и до сих пор у меня не было проблем (утечки памяти, нарушения доступа и т. Д.) От любых компиляторов. Только предупреждения из-за нестандартной работы.

Совершенно разумно удалить неизвестное (void *). Просто убедитесь, что указатель соответствует этим рекомендациям или может перестать иметь смысл:

1) Неизвестный указатель не должен указывать на тип, который имеет тривиальный деконструктор, и поэтому, когда он отображается как неизвестный указатель, он НИКОГДА НЕ ДОЛЖЕН УДАЛИТЬ. Удалите неизвестный указатель AFTER, вернув его обратно в тип ORIGINAL.

2) Указывается ли экземпляр как неизвестный указатель на связанную стек или связанную с кучей память? Если неизвестный указатель ссылается на экземпляр в стеке, он НИКОГДА НЕ ДОЛЖЕН УДАЛИТЬСЯ!

3) Вы на 100% уверены, что неизвестный указатель является допустимым регистром памяти? Нет, тогда НИКОГДА НЕ ДОЛЖНО БЫТЬ УДАЛЕНЫ!

В целом, существует очень мало непосредственной работы, которая может быть выполнена с использованием неизвестного типа (void *). Однако, косвенно, void * является отличным преимуществом для разработчиков C ++, чтобы полагаться, когда требуется двусмысленность данных.

Если вы просто хотите использовать буфер, используйте malloc / free. Если вы должны использовать new / delete, рассмотрите тривиальный class-оболочку:

 template struct size_buffer { char data_[ size_]; operator void*() { return (void*)&data_; } }; typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer OpaqueBuffer* ptr = new OpaqueBuffer(); delete ptr; 

Для частного случая char.

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

sizeof (char) обычно один, поэтому нет аргумента выравнивания. В случае с редкой платформой, где sizeof (char) не является одним, они выделяют выделенную память для своего символа. Поэтому аргумент выравнивания также является спорным.

malloc / free будет быстрее в этом случае. Но вы теряете std :: bad_alloc и должны проверить результат malloc. Вызов глобальных новых и удаленных операторов может быть лучше, поскольку он обходит среднего человека.

  • Как я могу получить размер массива из указателя в C?
  • Размер строки кешей L1 и L2
  • Мусорный коллектор MATLAB?
  • std :: вектор и непрерывная память многомерных массивов
  • Как узнать, указывает ли указатель на кучу или стек?
  • максимальная память, которую malloc может выделять
  • Как инициализировать память новым оператором в C ++?
  • Эффективные C ++ строки (интернирование, веревки, копирование на запись и т. Д.)
  • Размещение массива-new требует неопределенных накладных расходов в буфере?
  • Как получить доступную память C ++ / g ++?
  • C ++ Наведите несколько типов на вектор
  • Interesting Posts
    Давайте будем гением компьютера.