如果在一个成员函数中执行 "delete this;",会发生什么?

22

如果一个成员函数尝试执行delete this;,会发生什么情况,例如在以下类的构造函数中?

class A
{
public:
    A(int);
    ~A();
    int *pi;
}

A::A(int i)
{
    delete this;
    pi = new int(i);
}

A::~A()
{
    delete pi;
}

使用delete this;是可以的。但在构造函数中这样做以及在对象的字段之后访问它是未定义的行为。不幸的是,这通常不会导致处理器故障,只会导致堆破坏。 - Hans Passant
Als的回答很好地解释了允许这种情况的条件,根据这些条件,你的代码是不正确的。在构造函数内部,你可以抛出异常。 - Sebastian
4个回答

44

这个 C++ FAQ 条目回答得很好,如下所述:

只要小心谨慎,对一个对象进行delete this是可以的。

以下是我对“小心谨慎”的定义:

  • 你必须确信这个对象是通过new分配的(不是通过new[]、放置new、栈上的局部对象、全局变量或另一个对象的成员进行的分配;而是普通的new)。
  • 你必须确信你的成员函数将是在此对象上调用的最后一个成员函数。
  • 你必须确信在delete this之后,你成员函数的其余部分不会触及该对象的任何部分(包括调用任何其他成员函数或接触任何数据成员)。
  • 你必须确信在delete this之后,没有人会接触this指针本身。换句话说,你不能检查它,与另一个指针比较,将其与NULL比较,打印它,转换它或者对它执行任何操作。

你在访问pi之后进行了delete操作,违反了#3


我想你应该指出给定的示例不满足要求3,因为在构造函数中,在delete this之后访问了成员变量pi - Sebastian
@Sebastian:已完成,感谢您指出这一点,明确说明会更好。 - Alok Save
1
这可能是“合法的”,但它非常明显地表明你的设计有问题。资源管理和业务逻辑不应该交织在一起。一个能够自我管理的对象并不是一个正确构建的对象,因为它无法了解更大的上下文。最好将销毁的责任委托给一个管理者。与其自行销毁,不如向一个具有更深入理解上下文的管理对象注册自己以进行销毁。 - Martin York
@Martin:我不同意。在COM中(间接地,通过使用Release()),这个技巧用于确保delete使用分配对象时使用的相同内存分配器。当你输入它时,这肯定会引起某种警报,但至少有一个很好的理由使用这个结构。 - André Caron
@LokiAstari 这只是你的观点,不是吗?你有任何严肃的参考资料吗? - harper
显示剩余2条评论

3

不好的事情。你可以删除这个;但通常这是极其糟糕的想法,一旦完成,你就不能触及任何成员变量或成员函数。


1
你可以删除这个delete this,但前提是该实例是使用new创建的,并且您不再访问该实例的任何成员。
在您的示例中,如果您执行以下操作,则基本上会发生相同的情况。
class A
{
public:
    A(int);
    int *pi;
};

A::A(int i)
{
    pi = new int(i);
}

int main()
{
   A* p = new A(10);
   delete p;
   p->pi = new int; //bad stuff

//or
   A a(20);
   delete &a;  //bad stuff
   a.pi = new int; //more bad stuff
}

更糟糕的是,当你delete this时,pi成员未初始化,导致析构函数试图删除一个悬空指针。


1
通常情况下,在成员函数内部调用delete this是明确定义的,尽管有点冒险。
但是从构造函数中调用它可能是一个非常糟糕的想法(我不知道这是否被明确定义)。你为什么要这样做呢?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接