Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Может ли вызов удалить указатель, выделенный новым размещением?

Автор вопроса: NarekОтветов: 5

Можем ли мы вызвать delete для указателя, выделенного с помощью местоположения new? Если нет, то почему? Пожалуйста, объясните подробно.

Я знаю, что нет места размещения. Но мне интересно, почему просто оператор удаления не может удалить память, не заботясь о том, как выделена та память, на которую указывает указатель?

delete делает две вещи:

  1. Вызывает деструктор
  2. Освобождает память

И я не вижу причин для того, чтобы delete не мог вызвать ни одну из этих двух операций над объектом, который был создан путем размещения new. Есть идеи о причинах?

  • связанный вопрос fredoverflow 14.08.2011
  • Почему вы используете новое размещение? Какую потребность он удовлетворяет? Jonathan Grynspan 14.08.2011

c++memory-managementnew-operatorplacement-new

Ответы

Ответ 1 · рейтинг 5 · Remus Rusanu

EDIT1: я знаю, что нет места для удаления. Но мне интересно, почему просто оператор удаления не может удалить память, не заботясь о том, как выделена та память, на которую указывает указатель?

Поскольку каждая разновидность выделения памяти использует некоторую специфичную для реализации отслеживание памяти (обычно блок заголовка, который предшествует адресу пользователя), и это позволяет выделению/освобождению работать только при правильном сочетании:

  • new должен быть в паре с delete
  • new[] должен сочетаться с delete[] (хотя большинство реализаций простят смешение new и new[])
  • malloc и Фрид должны сочетаться с free
  • CoTaskMemAlloc пар с CoTaskMemFree
  • alloca пары ни с чем (об этом позаботится раскрутка стека)
  • MyCustomAllocator пар с MyCustomFree

Попытка вызвать неправильный делокатор приведет к непредсказуемому поведению (скорее всего, ошибка seg сейчас или позже). Поэтому вызов delete в памяти, выделенной чем-то другим, кроме new, приведет к плохим последствиям.

Кроме того, новое место размещения может быть вызвано по любому адресу, который может даже не быть выделенным адресом. Его можно вызвать по адресу, расположенному в середине какого-то более крупного объекта, его можно вызвать по области с отображением памяти, его можно вызвать по необработанной виртуальной фиксированной области, что угодно. delete попытается во всех этих случаях сделать то, что говорит ему его реализация: вычесть размер заголовка, интерпретировать его как заголовок new, связать его обратно с кучей. Кабум.

Тот, кто знает, как освободить память нового адреса размещения, это вы, поскольку вы точно знаете, как была выделена эта память. delete будет делать только то, что знает, и это может быть неправильно.

Ответ 2 · рейтинг 7 · Seth Carnegie

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

Например, в этом запутанном и обычно ненужном сценарии безопасно delete использовать память, на которой вы использовали размещение new, но только потому, что вы ранее выделили ее с помощью new:

char* mem = new char[sizeof(MyObject)];
MyObject* o = new (mem) MyObject;

// use o

o->~MyObject(); // with placement new you have to call the destructor by yourself

delete[] mem;

Однако это незаконно:

char mem[16]; // create a buffer on the stack, assume sizeof(MyObject) == 16

MyObject* o = new (mem) MyObject; // use stack memory to hold a MyObject
                                  // note that after placement new is done, o == mem
                                  // pretend for this example that the point brought up by Martin in the comments didn't matter

delete o; // you just deleted memory in the stack! This is very bad

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

Ответ 3 · рейтинг 5 · Antti

Нет, так как delete не только вызывает деструктор, но и освобождает память, но если вы использовали новое размещение, вы должны были выделить память самостоятельно, используя malloc() или стек. Однако вы должны сами вызвать деструктор. Также см. часто задаваемые вопросы по C++.

Ответ 4 · Kerrek SB

Нет. Выражение удаления места размещения отсутствует.

Типичный сценарий:

void * const addr = ::operator new(sizeof(T));  // get some memory

try {
  T * const pT = new (addr) T(args...);    // construct
  /* ... */
  p->~T();                                      // nap time
}
catch (...) {
}
::operator delete(addr);  // deallocate
                          // this is _operator_-delete, not a delete _expression_

Обратите внимание, что у нового оператора размещения есть соответствующий оператор удаления, который обязательно должен быть точно void ::operator delete(void* [, size_t]) { }, без операции; это то, что вызывается, если конструктор T выдает исключение.

Ответ 5 · Christian Rau

Нет, потому что новое размещение не выделяет никакой памяти. Вы используете новое размещение в ранее выделенной необработанной памяти. Единственное, что он делает, это вызывает конструктор объекта.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы