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

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

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

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;
}

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Для всех, кому небезразлично, насколько надежным может быть этот вид кодирования: если вы делаете телефонный звонок в, из или через почти любую часть Европы, есть очень хороший шанс, что он обрабатывается (по крайней мере частично) кодом это делает именно это.

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Ссылочный подсчет является сильной формой детерминированной сборки мусора - он гарантирует, что объекты управляют своим СОБСТВЕННЫМ временем жизни, а не полагаются на "умные" указатели и т.д., чтобы сделать это для них. Основной объект доступен только через интеллектуальные указатели "Ссылка", спроектированные так, чтобы указатели увеличивали и уменьшали целое число элементов (счетчик ссылок) в фактическом объекте.

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

Ключом к обеспечению безопасности является не предоставление пользователям доступа к CONSTRUCTOR объекта (его защита), а вместо этого обращение к некоторым статическим членам - FACTORY - как "static Reference CreateT (...)". Таким образом, вы ЗНАЕТЕ, что они всегда построены с обычным "новым" и что никакой необработанный указатель не доступен, поэтому "удалить это" никогда не будет взорваться.

Ответ 8

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

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

Ответ 9

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

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

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

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

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

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

Но НЕ освобождение, когда счетчик ссылок считается равным нулю в destructor 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;
      }
    }
 };

Хорошо, что-то в этом роде. Немного необычно иметь ссылочный подсчитанный тип указателя, который не удаляет автоматически объект, на который указывает rc'ed ptr destructor. Но похоже, что это могло бы сделать смешение голых указателей и 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;
        }
};

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

Ответ 10

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