Удалить это разрешено?


разрешено delete this; если delete-оператор является последним оператором, который будет выполняться на этом экземпляре класса? Конечно, я уверен, что объект, представленный this-указатель newly-создан.

Я думал о чем-то вроде этого:

void SomeModule::doStuff()
{
    // in the controller, "this" object of SomeModule is the "current module"
    // now, if I want to switch over to a new Module, eg:

    controller->setWorkingModule(new OtherModule());

    // since the new "OtherModule" object will take the lead, 
    // I want to get rid of this "SomeModule" object:

    delete this;
}

могу ли я это сделать?

10 198

10 ответов:

В C++ FAQ Lite есть запись специально для этого

Я думаю, что эта цитата подводит итог красиво

пока вы осторожны, это нормально для объекта, чтобы совершить самоубийство (удалить это).

да delete this; определил результаты, пока (как вы отметили) вы гарантируете, что объект был выделен динамически, и (конечно) никогда не пытайтесь использовать объект после его уничтожения. На протяжении многих лет было задано много вопросов о том, что стандарт говорит конкретно о delete this;, в отличие от удаления некоторых других указателей. Ответ на это довольно короткий и простой: он ничего не говорит. Он просто говорит, что deleteоперанд должен быть выражением, обозначающим a указатель на объект или массив объектов. Он входит в довольно много деталей о таких вещах, как то, как он выясняет, что (если таковые имеются) функция освобождения для вызова освободить память, но весь раздел на delete (§[expr.удалить]) не упоминает delete this; в частности на всех. В разделе о деструкторах упоминается delete this в одном месте (§[класс.dtor]/13):

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

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

во всяком случае, некоторые считают delete this противный Хак, и скажите всем, кто будет слушать этого следует избегать. Одной распространенной проблемой является трудность обеспечения того, чтобы объекты класса только динамически. Другие считают это вполне разумной идиомой и используют ее все время. Лично я где-то посередине: я редко использую его, но не стесняйтесь делать это, когда это кажется правильным инструментом для работы.

Edit: [в основном в ответ на комментарий @Alexandre C]: основное время, когда вы это делаете, - это объект, у которого есть жизнь, которая почти полностью свой собственный. Одним из примеров, приведенных Джеймсом Канзе, была система биллинга/отслеживания, над которой он работал в телефонной компании. В принципе, когда вы берете телефон, что-то принимает к сведению, что и создает

если это пугает вас, есть совершенно законный Хак:

void myclass::delete_me()
{
    std::unique_ptr<myclass> bye_bye(this);
}

Я думаю delete this является идиоматическим C++, хотя, и я представляю это только как любопытство.

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

void myclass::throw_error()
{
    std::unique_ptr<myclass> bye_bye(this);
    throw std::runtime_exception(this->error_msg);
}

Примечание: Если вы используете компилятор старше C++11 вы можете использовать std::auto_ptr вместо std::unique_ptr, он будет делать то же самое.

одна из причин, по которой был разработан C++, заключалась в том, чтобы упростить повторное использование кода. В общем случае C++ должен быть написан так, чтобы он работал независимо от того, создается ли экземпляр класса в куче, в массиве или в стеке. "Удалить это" - очень плохая практика кодирования, потому что она будет работать только в том случае, если в куче определен один экземпляр; и лучше бы не было другого оператора delete, который обычно используется большинством разработчиков для очистки кучи. Это также предполагает, что нет обслуживания программист в будущем вылечит ложно воспринимаемую утечку памяти, добавив оператор delete.

даже если вы заранее знаете, что ваш текущий план состоит в том, чтобы выделить только один экземпляр в куче, что делать, если какой-то беспечный разработчик придет в будущем и решит создать экземпляр в стеке? Или, что если он вырезает и вставляет определенные части класса в новый класс, который он намерен использовать в стеке? Когда код достигнет "удалить это" он погаснет и удалить его, но тогда, когда объект выходит из области видимости, вызывается деструктор. Деструктор будет пытаться удалить его снова, а затем вы поливаете из шланга. В прошлом, делая что-то подобное будет испортить не только программу, но и операционную систему и компьютер должен быть перезагружен. В любом случае, это крайне не рекомендуется и следует избегать. Я должен был бы быть отчаянным, серьезно оштукатуренным или действительно ненавидеть компанию, в которой я работал, чтобы написать код, который сделал этот.

это разрешено (только не использовать объект после этого), но я бы не стал писать такой код на практике. Я думаю, что delete this должны появляться только в функциях, которые вызвали release или Release и выглядит так: void release() { ref--; if (ref<1) delete this; }.

Ну, в компонентной объектной модели (COM) delete this конструкция может быть частью Release метод, который вызывается всякий раз, когда вы хотите освободить объект aquisited:

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}

вы можете это сделать. Однако, вы не можете назначить это. Таким образом, причина, по которой вы заявляете для этого: "я хочу изменить мнение", кажется очень сомнительной. Лучший метод, на мой взгляд, был бы для объекта, который содержит представление, чтобы заменить это представление.

конечно, вы используете объекты RAII, и поэтому вам вообще не нужно вызывать delete...верно?

Это основная идиома для объектов с подсчетом ссылок.

подсчет ссылок-это сильная форма детерминированной сборки мусора - она гарантирует, что объекты управляют своим собственным временем жизни вместо того, чтобы полагаться на "умные" указатели и т. д. чтобы сделать это для них. Доступ к базовому объекту осуществляется только с помощью интеллектуальных указателей "Reference", разработанных таким образом, чтобы указатели увеличивали и уменьшали целое число элементов (счетчик ссылок) в фактическом объекте.

когда последняя ссылка падает из стека или удаляется, счетчик ссылок будет идти к нулю. Поведение вашего объекта по умолчанию будет тогда вызовом " удалить это "для сбора мусора - библиотеки, которые я пишу, предоставляют защищенный виртуальный вызов" CountIsZero " в базовом классе, чтобы вы могли переопределить это поведение для таких вещей, как кэширование.

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

Это старый, ответный вопрос, но @Alexandre спросил: "почему кто-то хочет это сделать?", и я подумал, что могу привести пример использования, который я рассматриваю сегодня днем.

устаревший код. Использование голых указателей в obj*obj с удаления параметр obj в конце.

к сожалению, мне нужно иногда, не часто, чтобы сохранить объект в живых дольше.

Я рассматриваю возможность сделать его ссылочным подсчитанным интеллектуальным указателем. Но было бы много кода для изменения, если я должен был использовать ref_cnt_ptr<Obj> везде. И если вы смешиваете naked Obj* и ref_cnt_ptr, вы можете получить объект неявно удален, когда последний ref_cnt_ptr уходит, даже если есть Obj* все еще жив.

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

увеличение и уменьшение счетчика ссылок как explicit_delete_ref_cnt_ptr получить манипулировать.

но не освобождается, когда счетчик ссылок считается равным нулю в деструкторе explicit_delete_ref_cnt_ptr.

только освобождение, когда счетчик ссылок считается равным нулю в явной операции удаления. Например, в чем-то вроде:

template<typename T> class explicit_delete_ref_cnt_ptr { 
 private: 
   T* ptr;
   int rc;
   ...
 public: 
   void delete_if_rc0() {
      if( this->ptr ) {
        this->rc--;
        if( this->rc == 0 ) {
           delete this->ptr;
        }
        this->ptr = 0;
      }
    }
 };

ОК, что-то подобное. Это немного необычно имейте тип указателя с подсчетом ссылок, который не удаляет автоматически объект, на который указывает деструктор PTR rc'Ed. Но похоже, что это может сделать смешивание голых указателей и указателей rc'Ed немного безопаснее.

но пока нет необходимости удалять это.

но потом мне пришло в голову: если объект, на который указывает указатель, знает, что он подсчитывается, например, если счетчик находится внутри объекта (или в какой-либо другой таблице), то процедура delete_if_rc0 может быть методом объект pointee, а не (умный) указатель.

class Pointee { 
 private: 
   int rc;
   ...
 public: 
   void delete_if_rc0() {
        this->rc--;
        if( this->rc == 0 ) {
           delete this;
        }
      }
    }
 };

на самом деле, это не должен быть метод-член вообще, но может быть свободной функцией:

map<void*,int> keepalive_map;
template<typename T>
void delete_if_rc0(T*ptr) {
        void* tptr = (void*)ptr;
        if( keepalive_map[tptr] == 1 ) {
           delete ptr;
        }
};

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

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