Может ли вызов удалить указатель, выделенный новым размещением?
Автор вопроса: NarekОтветов: 5
Можем ли мы вызвать delete для указателя, выделенного с помощью местоположения new? Если нет, то почему? Пожалуйста, объясните подробно.
Я знаю, что нет места размещения. Но мне интересно, почему просто оператор удаления не может удалить память, не заботясь о том, как выделена та память, на которую указывает указатель?
delete делает две вещи:
Вызывает деструктор
Освобождает память
И я не вижу причин для того, чтобы delete не мог вызвать ни одну из этих двух операций над объектом, который был создан путем размещения new. Есть идеи о причинах?
связанный вопрос fredoverflow 14.08.2011
Почему вы используете новое размещение? Какую потребность он удовлетворяет? Jonathan Grynspan 14.08.2011
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
Нет, потому что новое размещение не выделяет никакой памяти. Вы используете новое размещение в ранее выделенной необработанной памяти. Единственное, что он делает, это вызывает конструктор объекта.