删除这个允许吗?
是否允许delete this;
如果delete语句是将在该类的该实例上执行的最后一个语句? 当然,我确定this
指针代表的对象是new
创建的。
我正在考虑这样的事情:
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; }
我可以这样做吗?
C ++ FAQ Lite有专门的一个入口
我认为这个引用很好地概括了它
只要你小心,对象自杀就行(删除这个)。
是的, delete this;
已经定义了结果,只要(如你所注意到的)你确保对象是动态分配的,并且(当然)不会试图在对象被销毁之后使用它。
有人认为这是一个讨厌的黑客,并告诉任何人谁会听,应该避免。 一个常见的问题是难以确保只有动态分配类的对象。 其他人认为这是一个非常合理的习惯用法,并一直使用它。 就我个人而言,我处于中间的某个地方:我很少使用它,但是当它似乎是正确的工具时,请不要犹豫。
编辑:[主要是回应@亚历山大C的评论]:你这样做的主要时间是一个对象,有一个几乎完全是自己的生活。 James Kanze引用的一个例子是他为电话公司工作的计费/跟踪系统。 基本上,当你拿起电话,东西注意到这一点,并创建一个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
,它将执行相同的操作。
这是允许的(只是不要使用该对象之后),但我不会写这样的代码在实践中。 我认为delete this
应该只出现在称为release
或Release
功能,看起来像: void release() { ref--; if (ref<1) delete this; }
void release() { ref--; if (ref<1) delete this; }
void release() { ref--; if (ref<1) delete this; }
。
C ++设计的原因之一就是使代码重用变得容易。 一般来说,应该编写C ++,以使它能够在堆,数组或堆栈上实例化。 “删除这个”是一个非常糟糕的编码实践,因为只有在堆上定义了一个实例才能工作。 并且最好不要使用另一个删除语句,这是大多数开发人员通常用来清理堆的。 这样做也假定在将来没有维护程序员会通过添加删除语句来纠正错误的内存泄漏。
即使你事先知道你目前的计划是只在堆上分配一个实例,如果一个幸运的开发者在将来出现并决定在栈上创建一个实例会怎样? 或者,如果他将班级的某些部分剪切粘贴到他打算在堆栈上使用的新班级上呢? 当代码达到“删除这个”时,它会关闭并删除它,但是当这个对象超出范围时,它会调用析构函数。 然后析构函数将尝试再次删除它,然后你被洗净。 过去,做这样的事情不仅会使程序崩溃,而且操作系统和计算机也需要重新启动。 无论如何,这是非常不推荐的,几乎总是应该避免的。 我将不得不绝望,严重地抹黑,或真的讨厌我工作的公司编写这样做的代码。
那么,在组件对象模型(COM)中delete this
构造就可以成为Release
方法的一部分, delete this
方法在任何时候你想要释放的对象都被调用:
void IMyInterface::Release() { --instanceCount; if(instanceCount == 0) delete this; }
你可以这样做。 但是,你不能分配给这个。 因此,你说这样做的原因,“我想改变看法”,似乎是非常可疑的。 在我看来,更好的方法应该是让持有观点的对象取代这种观点。
当然,你使用的是RAII对象,所以你根本不需要调用delete …对吗?
这是引用计数对象的核心成语。
引用计数是确定性垃圾收集的一种强有力的形式 – 它确保对象管理自己的生命期,而不是依靠“智能”指针等来为他们做这件事。 底层对象只能通过“引用”智能指针进行访问,这种智能指针被设计成使得指针递增和递减实际对象中的成员整数(引用计数)。
当最后一个参考值下降或被删除时,参考计数将变为零。 您的对象的默认行为将是一个调用“删除这个”垃圾收集 – 我写的库提供了一个受保护的虚拟“CountIsZero”调用在基类,以便您可以覆盖这种行为,如缓存。
使这个安全的关键是不允许用户访问有问题的对象的CONSTRUCTOR(使其受到保护),而是让他们调用一些静态成员 – 工厂 – 像“静态引用CreateT(…)”。 那样的话,你肯定知道他们总是用普通的“新”建造,而且没有任何原始的指针可用,所以“删除这个”永远不会被炸毁。
这是一个古老的答案,但亚历山大问道:“为什么有人想这样做呢?”我想我可以提供一个我今天下午正在考虑的例子。
遗留代码。 在最后使用裸指针Obj * obj和delete obj。
不幸的是,我有时需要,而不是经常保持对象活得更久。
我正在考虑把它作为智能指针的参考。 但是如果我要在所有地方都使用ref_cnt_ptr<Obj>
,将会有很多代码需要更改。 如果你混合使用了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; } } };
好的,就像那样。 有一个引用计数指针类型不自动删除rc'ed ptr析构函数中指向的对象是有点不寻常的。 但是看起来这样做可能会让混合裸指针和rc指针更安全一些。
但到目前为止没有必要删除这个。
但是后来我发现:如果指向的对象知道它是被引用计数的,例如,如果计数在对象内(或在其他表中),则例程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; } };
(顺便说一下,我知道代码是不正确的 – 如果我添加所有的细节,它变得不太可读,所以我就这样离开。)
只要对象在堆中,删除这个就是合法的。 你需要要求对象只是堆。 唯一的方法就是使析构函数保护 – 这种方法可以只从类中调用,所以你需要一个确保删除的方法