Удалить это разрешено?
разрешено delete this;
если delete-оператор является последним оператором, который будет выполняться на этом экземпляре класса? Конечно, я уверен, что объект, представленный this
-указатель new
ly-создан.
Я думал о чем-то вроде этого:
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
операнд должен быть выражением, обозначающим 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; } };
(кстати, я знаю, что код не совсем правильный - он становится менее читаемым, если я добавляю все детали, поэтому я оставляю его так.)