Какая польза от «удалить это»? - PullRequest
28 голосов
/ 15 января 2009

Сегодня я видел какой-то устаревший код. В деструкторе есть утверждение типа "delete this". Я думаю, этот вызов будет рекурсивным. Почему это работает?

Я сделал быстрый поиск по Y !, я обнаружил, что если есть необходимость ограничить пользователя в создании стекового объекта, мы можем сделать деструктор приватным и предоставить интерфейс для удаления экземпляра. В предоставленном интерфейсе мы должны вызвать delete для этого указателя.

Существуют ли другие ситуации для использования таких утверждений?

Ответы [ 4 ]

32 голосов
/ 15 января 2009

"удалить это" обычно используется для объектов с подсчетом ссылок. Для объекта с подсчетом ссылок решение о том, когда удалять, обычно размещается на самом объекте. Вот пример того, как будет выглядеть метод Release [1].

int MyRefCountedObject::Release() {
  _refCount--;
  if ( 0 == _refCount ) {
    delete this;
    return 0;
  }
  return _refCount;
}

ATL COM-объекты являются ярким примером этого шаблона.

[1] Да, я понимаю, что это не потокобезопасно.

24 голосов
/ 15 января 2009

delete this недопустимо в деструкторе. Это может быть использовано в другом месте. Но это редко хорошая идея. Фреймворк wxWidgets использует его для своего класса потока. У него есть режим, в котором, когда поток завершает выполнение, он автоматически освобождает системные ресурсы и себя (объект wxThread). Я нахожу это очень раздражающим, потому что со стороны вы не можете знать, действительно ли это ссылка или нет - вы больше не можете вызывать функцию, подобную IsValid, потому что объект не существует. Это пахнет как основная проблема с delete this, за исключением проблемы с тем, что его нельзя использовать для нединамических объектов.

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

В C ++ FAQ есть запись об этом. C ++ Стандартная цитата по моей претензии выше (3.8p5):

До того, как началось время жизни объекта, но после того, как было выделено хранилище, которое будет занимать объект, или, после того, как закончился срок жизни объекта, и до того, как хранилище, которое занимал объект, будет повторно использовано или освобождено, любой указатель, который ссылается к месту хранения, где объект будет или был расположен, может быть использован, но только ограниченным образом. [...] Если объект будет или имел тип класса с нетривиальным деструктором, и указатель используется в качестве операнда выражения удаления, программа имеет неопределенное поведение.

Срок службы заканчивается, когда деструктор объекта начинает выполнение. Обратите внимание, что существуют исключения из правил, следующих за этим абзацем для объектов, находящихся в процессе строительства и уничтожения (например, вам разрешен доступ к нестатическим элементам данных), подробности см. В 12.7.

3 голосов
/ 15 января 2009

Там, где считаются вескими причинами сделать это в первые дни C ++. Например, самостоятельное удаление объекта с подсчетом ссылок (как говорит JaredPar). Насколько я знаю, все они были признаны плохой идеей в долгосрочной перспективе.

2 голосов
/ 30 января 2010

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

void RemoveAndDeallocate()
{
    LinkedListNode *current_prev = prev, *current_next = next;
    current_prev->next = current_next;
    current_next->prev = current_prev;
    delete this;
}

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

...