是否允许删除?

Mar*_*aux 225 c++ memory-management new-operator self-destruction delete-operator

是否允许delete this;delete-statement是将在该类实例上执行的最后一个语句?当然,我确信this-pointer所代表的对象是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;
}
Run Code Online (Sandbox Code Playgroud)

我可以这样做吗?

Jar*_*Par 231

C++ FAQ Lite专门为此提供了一个条目

我认为这句话总结得很好

只要你小心,对象就可以自杀(删除它).

  • 相应的FQA也有一些有用的评论:http://yosefk.com/c++fqa/heap.html#fqa-16.15 (14认同)
  • 为了安全起见,您可以在原始对象上使用私有析构函数,以确保它不是在堆栈上构造的,也不是作为数组或向量的一部分构造的。 (3认同)
  • 链接的常见问题解答文章中定义了"小心".(虽然FQA链接主要是咆哮 - 就像几乎所有内容一样 - C++有多糟糕) (3认同)

Jer*_*fin 83

是的,delete this;已经定义了结果,只要(如你所知)你确保对象是动态分配的,并且(当然)在它被销毁后永远不会尝试使用该对象.多年来,人们一直在询问标准所说的具体内容delete this;,而不是删除其他指针.答案是相当简短的:它并没有说太多.它只是说delete操作数必须是一个表达指向对象或对象数组的指针.它涉及到很多细节,比如它如何确定调用释放内存的释放函数(如果有的话),但是delete(§[expr.delete])的整个部分根本没有delete this;具体提及.关于破坏者的部分确实提到了delete this 在一个地方(§[class.dtor]/13):

在定义虚拟析构函数(包括隐式定义(15.8))时,非数组释放函数被确定为对于表达式delete,它出现在析构函数类的非虚析构函数中(见8.3.5) ).

这往往支持标准认为delete this;有效的想法- 如果它无效,其类型将没有意义.delete this;据我所知,这是标准提到的唯一地方.

无论如何,有些人会考虑delete this讨厌的黑客行为,并告诉任何会听取应该避免的人.一个常被引用的问题是难以确保仅动态分配类的对象.其他人认为这是一个非常合理的习惯用法,并且一直使用它.就个人而言,我处于中间位置:我很少使用它,但是当它似乎是适合这项工作的工具时,请不要犹豫.

使用这种技术的主要时间是一个具有几乎完全属于它自己的生命的对象.James Kanze引用的一个例子是他为电话公司工作的计费/跟踪系统.当您开始拨打电话时,会注意到这一点并创建一个phone_call对象.从那时起,该phone_call对象处理电话呼叫的详细信息(在拨号时建立连接,向数据库添加条目以说明呼叫何时开始,如果您进行电话会议,可能会连接更多人等)呼叫中的最后一个人挂起,该phone_call对象进行最后的簿记(例如,向数据库添加一个条目,说明你何时挂机,这样他们就可以计算你的呼叫时间长度)然后自行销毁.的一生phone_callobject是基于第一个人何时开始调用以及最后一个人何时离开调用 - 从系统其余部分的角度来看,它基本上是完全随意的,所以你不能将它绑定到代码中的任何词法范围或该订单上的任何内容.

对于任何可能关心这种编码多么可靠的人来说:如果你打电话给欧洲几乎任何一个地方,或者通过欧洲几乎任何一个地方打电话,那么很有可能它被代码处理(至少部分)这正是这样做的.

  • 谢谢,我会把它放在我记忆中的某个地方.我假设您将构造函数和析构函数定义为私有,并使用一些静态工厂方法来创建此类对象. (2认同)

Mar*_*som 46

如果它让你害怕,那就是完全合法的黑客攻击:

void myclass::delete_me()
{
    std::unique_ptr<myclass> bye_bye(this);
}
Run Code Online (Sandbox Code Playgroud)

我认为delete this是惯用的C++,我只是把它作为一种好奇心来表达.

有一种情况,这种结构实际上很有用 - 您可以在抛出需要来自对象的成员数据的异常后删除该对象.在投掷发生之前,该对象仍然有效.

void myclass::throw_error()
{
    std::unique_ptr<myclass> bye_bye(this);
    throw std::runtime_exception(this->error_msg);
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您使用的是比C++ 11更早的编译器,您可以使用std::auto_ptr而不是std::unique_ptr,它将执行相同的操作.


Bob*_*yan 24

设计C++的原因之一是使重用代码变得容易.通常,应该编写C++,以便无论是在堆上,在数组中还是在堆栈上实例化类,它都能正常工作."删除这个"是一个非常糟糕的编码实践,因为只有在堆上定义了单个实例时它才会起作用; 并且最好不要使用另一个删除语句,大多数开发人员通常使用它来清理堆.这样做还假设未来的维护程序员不会通过添加删除语句来治愈错误感知的内存泄漏.

即使你事先知道你当前的计划是只在堆上分配一个实例,如果将来会有一些幸运的开发人员出现并决定在堆栈上创建一个实例呢?或者,如果他将类的某些部分剪切并粘贴到他打算在堆栈中使用的新类,该怎么办?当代码到达"删除它"时,它将关闭并删除它,但是当对象超出范围时,它将调用析构函数.析构函数然后会尝试再次删除它,然后你就被软管了.在过去,做这样的事情不仅会破坏程序,而且还需要重新启动操作系统和计算机.无论如何,这是非常不推荐的,几乎总是应该避免.我必须绝望,严肃地涂抹,或者真的讨厌我为编写这样做的代码而工作的公司.

  • +1.我不明白为什么你被投票了."无论是在堆上,在数组中还是在堆栈上实例化类,都应该编写C++以使其工作"是非常好的建议. (6认同)

Kir*_*sky 20

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


Unk*_*osu 14

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

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}
Run Code Online (Sandbox Code Playgroud)


小智 8

这是引用计数对象的核心习语.

引用计数是确定性垃圾收集的一种强大形式 - 它确保对象管理其OWN生命周期,而不是依赖于"智能"指针等来为它们执行.底层对象只能通过"引用"智能指针访问,这些指针的设计使得指针增加和减少实际对象中的成员整数(引用计数).

当最后一个引用从堆栈中删除或被删除时,引用计数将变为零.然后,对象的默认行为是对"删除此"进行垃圾收集调用 - 我编写的库在基类中提供受保护的虚拟"CountIsZero"调用,以便您可以为缓存之类的操作覆盖此行为.

使这个安全的关键是不允许用户访问相关对象的CONSTRUCTOR(使其受到保护),而是让他们调用一些静态成员 - 类似于FACTORY的"静态参考CreateT(...)".这样你就知道它们总是用普通的"新"构建,并且没有原始指针可用,所以"删除它"不会爆炸.


Edw*_*nge 7

你可以这样做.但是,您无法分配给此.因此,您声明这样做的原因,"我想改变观点",似乎非常值得怀疑.在我看来,更好的方法是保持视图的对象替换该视图.

当然,你正在使用RAII对象,所以你根本不需要调用删除...对吗?