Можно ли удалить это?

Это разрешено 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 ответов

Решение

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

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

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

Да, 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 объект обрабатывает детали телефонного звонка (установление соединения при наборе номера, добавление записи в базу данных, чтобы сказать, когда начался звонок, возможно, подключить больше людей, если вы делаете конференц-связь, и т. д.), когда последний человек звонит повесить phone_call Объект ведет окончательный учет (например, добавляет запись в базу данных, чтобы сказать, когда вы положили трубку, чтобы они могли вычислить, как долго был ваш звонок), а затем уничтожает себя. Время жизни phone_call Объект основан на том, когда первый человек начинает вызов, и когда последние люди покидают вызов - с точки зрения остальной части системы, это в основном совершенно произвольно, поэтому вы не можете связать его с какой-либо лексической областью действия в коде. или что-нибудь в этом порядке.

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

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

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 this должен появляться только в функциях, которые вызвали release или же Release и выглядит так: void release() { ref--; if (ref<1) delete this; },

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

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

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

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

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

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

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

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

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

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

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

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

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

Увеличивая и уменьшая счетчик ссылок, как явным образом манипулируют явным_далетом_ref_cnt_ptr.

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

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

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

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

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

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

Другие вопросы по тегам