Можно ли удалить вызов по указателю, который выделен при размещении нового? - PullRequest
5 голосов
/ 14 августа 2011

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

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

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

  1. Calls destrucor
  2. Освобождает память

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

Ответы [ 5 ]

7 голосов
/ 14 августа 2011

Вы должны вызывать 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 не может иметь дело с это.

5 голосов
/ 14 августа 2011

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

4 голосов
/ 15 августа 2011

РЕДАКТИРОВАТЬ1: я знаю, что нет места размещения удалить. Но мне интересно, почему просто удалить оператор не может удалить память, не заботясь о том, как это память, на которую выделяются указатели?

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

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

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

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

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

0 голосов
/ 14 августа 2011

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

0 голосов
/ 14 августа 2011

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

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

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 выдает исключение.

...