Что такое "удалить это"?
Сегодня я видел некоторый унаследованный код. В деструкторе есть выражение типа "delete this
". Я думаю, этот вызов будет рекурсивным. Почему он работает?
Я сделал быстрый поиск по Y!, я обнаружил, что если есть необходимость ограничить пользователя созданием объекта стека, мы можем сделать destructor private и предоставить интерфейс для удаления экземпляра. В предоставленном интерфейсе мы должны вызвать delete на этом указателе.
Существуют ли другие ситуации для использования таких утверждений?
Ответы
Ответ 1
"удалить это" обычно используется для подсчитанных объектов. Для подсчитанного объекта решение о том, когда удалить, обычно помещается на самом объекте. Вот пример того, как будет выглядеть метод Release [1].
int MyRefCountedObject::Release() {
_refCount--;
if ( 0 == _refCount ) {
delete this;
return 0;
}
return _refCount;
}
Объекты ATL COM являются ярким примером этого шаблона.
[1] Да, я понимаю, что это не безопасно для потоков.
Ответ 2
delete this
недействителен в деструкторе. Его можно использовать в другом месте. Но это редко бывает хорошей идеей. Структура wxWidgets
использует его для своего класса потоков. Он имеет режим, когда при завершении выполнения потока он автоматически освобождает системные ресурсы и сам (объект wxThread). Мне было очень неприятно, потому что извне вы не можете знать, действительно ли это относится к ней или нет - вы больше не можете вызывать функцию типа IsValid
, потому что объект не существует. Это пахнет главной проблемой с delete this
, кроме проблемы, которая не может быть использована для нединамических объектов.
Если вы это сделаете, убедитесь, что вы не касаетесь какого-либо элемента данных или больше не вызываете какую-либо функцию-член на удаленном объекте. Лучше всего это делать как последнее утверждение в не виртуальной, защищенной или частной функции. Вызов delete также действителен в виртуальной и/или публичной функции, но я бы ограничил видимость метода, выполняющего это.
С++ FAQ содержит запись об этом. Стандартная цитата на С++ по моей претензии выше (3.8p5
):
До того, как срок жизни объекта запустился, но после того, как хранилище, которое будет занимать объект, было выделено или, после того, как срок жизни объекта закончился и до того, как хранилище, которое объект занят, повторно используется или выпущен, любой указатель, который ссылается к месту хранения, в котором будет находиться или находится объект, может использоваться только ограниченным образом. [...] Если объект будет или имеет тип класса с нетривиальным деструктором, а указатель используется как операнд выражения-удаления, программа имеет поведение undefined.
Срок службы заканчивается, когда деструктор объекта начинает выполнение. Обратите внимание, что существуют исключения из правил, следующих за этим абзацем, для объектов, находящихся в стадии строительства и уничтожения (например, вам разрешен доступ к нестатическим элементам данных), подробно описанный в 12.7
.
Ответ 3
Там, где это считается хорошим основанием для этого в ранние дни С++. Например, само удаление объекта, подсчитанного ref (как говорит JaredPar).
Насколько мне известно, в конечном итоге все они оказались плохой идеей.
Ответ 4
В двусвязном списке можно удалить node без ссылки на какую-либо внешнюю структуру, такую как объект "списка" высокого уровня. Это делает разумным для каждого node обрабатывать свое собственное освобождение (потенциально связанное с дополнительным статическим методом для обработки начального распределения из того же пула памяти). В этой ситуации может быть смысл для объекта node удалить себя (по запросу пользователя).
void RemoveAndDeallocate()
{
LinkedListNode *current_prev = prev, *current_next = next;
current_prev->next = current_next;
current_next->prev = current_prev;
delete this;
}
Несмотря на это, разумно, чтобы node был отсоединен от одного списка и привязан к другому списку, не освобождая память, поэтому не желательно, чтобы одна операция удаления безоговорочно освобождала память.