是“删除这个”;c++中允许的?

如果delete语句是将在该类实例上执行的最后一条语句,是否允许delete this; ?当然,我确信由__abc1 -指针表示的对象是newly-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;
}

我能这样做吗?

125763 次浏览

c++ FAQ Lite中有一个专门的条目

我认为这句话很好地总结了这一点

只要你小心,对象自杀是可以的(删除这个)。

是的,delete this;已经定义了结果,只要(正如你所注意到的)你确保对象是动态分配的,并且(当然)永远不会尝试在对象销毁后使用它。在过去的几年里,许多问题都被问到标准对delete this;具体说了什么,而不是删除一些其他指针。这个问题的答案相当简短:它并没有说明什么。它只是说delete的操作数必须是一个表达式,该表达式指定指向对象或对象数组的指针。它涉及了相当多的细节,比如它如何找出要调用什么(如果有的话)释放内存的函数,但关于delete(§[express .delete])的整个部分根本没有特别提到delete this;。关于析构函数的部分在一个地方提到了delete this(§[class.dtor]/13):

在定义虚析构函数时(包括隐式定义(15.8)),非数组释放函数就像为析构函数类的非虚析构函数中出现的表达式delete this确定一样(参见8.3.5)。

这倾向于支持标准认为delete this;是有效的想法——如果它是无效的,它的类型就没有意义。据我所知,这是标准中唯一提到delete this;的地方。

不管怎样,一些人认为delete this是一个讨厌的黑客,并告诉任何愿意听的人应该避免。一个经常被引用的问题是很难确保类的对象只被动态分配。其他人认为这是一个非常合理的习语,并一直在使用它。就我个人而言,我介于两者之间:我很少使用它,但当它似乎是适合这项工作的工具时,不要犹豫。

使用这种技术的主要时间是一个对象,它几乎完全是自己的生命。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,它会做同样的事情。

你可以这样做。然而,你不能给它赋值。因此,你陈述这样做的原因,“我想改变观点”,似乎很有问题。在我看来,更好的方法是让持有视图的对象替换该视图。

当然,你使用的是RAII对象所以你根本不需要调用delete,对吧?

这是允许的(只是在那之后不要使用对象),但我不会在实践中编写这样的代码。我认为delete this应该只出现在调用releaseRelease的函数中,看起来像:void release() { ref--; if (ref<1) delete this; }

在组件对象模型(COM)中,delete this构造可以是Release方法的一部分,当你想释放被获取的对象时调用该方法:

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

这是一个老问题,但@Alexandre问“为什么有人想这样做?”,我想我可以提供一个示例用法,我今天下午正在考虑。

遗留代码。使用裸指针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'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;
}
};

(顺便说一句,我知道代码不太正确-如果我添加所有的细节,它变得不那么可读,所以我就这样留下它。)

c++被设计出来的原因之一是使代码易于重用。一般来说,c++应该被编写成无论类实例化在堆上、数组中还是堆栈上都能正常工作。“Delete this”是一个非常糟糕的编码实践,因为它只在堆上定义了一个实例时才会起作用;并且最好不要使用另一个delete语句,大多数开发人员通常使用该语句来清理堆。这样做还假定将来没有维护程序员会通过添加delete语句来纠正错误的内存泄漏。

即使您事先知道当前的计划是只在堆上分配一个实例,但如果将来某个乐天派的开发人员决定在堆栈上创建一个实例,该怎么办呢?或者,如果他将类的某些部分剪切并粘贴到他打算在堆栈上使用的新类中呢?当代码到达“delete this”时,它会自动删除它,但是当对象超出作用域时,它会调用析构函数。析构函数将尝试再次删除它,然后你就被删除了。在过去,这样做不仅会破坏程序,还会破坏操作系统,并且需要重新启动计算机。在任何情况下,这都是非常不推荐的,几乎应该总是避免。除非我非常绝望,喝得烂醉如泥,或者非常讨厌我工作的公司,我才会编写这样的代码。

这是引用计数对象的核心习惯用法。

引用计数是一种强大的确定性垃圾收集形式——它确保对象管理自己的生命周期,而不是依赖“智能”指针等来为它们做这些。底层对象只能通过“Reference”智能指针访问,设计为指针在实际对象中递增或递减一个成员整数(引用计数)。

当最后一个引用退出堆栈或被删除时,引用计数将归零。对象的默认行为将是调用“delete this”来进行垃圾收集——我写的库在基类中提供了一个受保护的虚拟“CountIsZero”调用,这样你就可以为缓存之类的事情覆盖这个行为。

确保安全的关键是不允许用户访问相关对象的构造函数(使其受保护),而是让他们调用一些静态成员——FACTORY——比如“static Reference CreateT(…)”。通过这种方式,你可以确定它们总是用普通的“new”构建的,并且没有原始指针可用,所以“delete this”永远不会爆炸。

只要对象在堆中,删除这个是合法的。 您需要要求对象仅为堆。 做到这一点的唯一方法是使析构函数受保护——这种方法可以从类中调用only delete,因此您需要一个方法来确保delete