2012-01-11 18 views
10

请考虑下面的代码`delete this;`语句期间发生了什么?在以下两个选项</p> <pre><code>class foo { public: foo(){} ~foo(){} void done() { delete this;} private: int x; }; </code></pre> <p>正在发生的事情(而且是有效的吗?):

选项1:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
    delete a; 
} 

选项2:

void main() 
{ 
    foo a; 
    a.done(); 
} 

请问第二个delete a;声明在opt离子1会导致异常或堆腐败?

option2是否会导致异常或堆损坏?

+0

您是否错误地忘记了该类的开头括号,还是完全是复制粘贴代码? – Neophile 2012-01-11 13:46:06

+0

@Nerds:错字 - 修正... – NirMH 2012-01-11 13:46:50

+0

有趣。我猜第一个会导致segfault或堆corrupetion,第二个会做任何删除指向堆栈的指针。 – cha0site 2012-01-11 13:50:17

回答

19

delete this;是允许的,它删除对象。

这两个代码片段都具有未定义的行为 - 第一种情况是删除已删除的对象,第二种情况是删除具有自动存储持续时间的对象。

由于行为未定义,因此标准并未说明它们是否会导致异常或堆损坏。对于不同的实现,它可能既不是,也不是两者都有,并且每次运行代码时可能也可能不一样。

+0

希望它会是前者。 – Xeo 2012-01-11 13:55:12

+1

未定义的行为,最好的和正确的答案。 +1 – 2012-01-11 13:57:20

4

两者都会导致错误。

第一指针被删除两次,与第二delete而第二个是在堆栈上分配造成错误并且不能被隐式地删除(通过析构函数的第一次调用引起的误差)。

+0

你打败了我! – Neophile 2012-01-11 13:48:30

+0

我编辑了这个问题。我认为它应该分配在堆上,然后用'done'删除。这种情况发生了什么?在堆栈上删除没有意义,可能不会被OP询问。 – duedl0r 2012-01-11 13:51:12

+0

你的回答是正确的,但问题是会发生什么样的错误。 – cha0site 2012-01-11 13:52:07

1

双方将导致一个错误,你想要的是:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
} 

,编译器将扩​​展到类似下面,我希望品牌删去“本”少了几分扑朔迷离。

void __foo_done(foo* this) 
{ 
    delete this; 
} 

void main() 
{ 
    foo* a = new foo(); 
    __foo_done(a); 
} 

参见,Is it legal (and moral) for a member function to say delete this?

+0

如果它是标准布局(或POD)类中的对象的第一个成员,该对象是使用纯新分配的,并且没有其他析构函数呢? (不,这不会是_inane_,但是,...) – Random832 2012-01-11 15:54:46

+0

@ Random832:我不明白你的问题,对不起。 – ronag 2012-01-11 15:59:19

+0

您链接的FAQ问题提供了一个条件列表,其中'delete this'是安全的。由于标准布局类的第一个成员与父对象本身具有相同的地址,因此理论上这似乎是另一种情况。 – Random832 2012-01-11 16:01:13

1

调用delete this是一个坏主意。无论谁打电话new应拨打delete。因此,其他答复中突出强调的问题。

也可以在构造对象数组时使用内存泄漏/未定义的行为。

+0

为什么要调用'delete this'是一个坏主意?我会说在很多(大多数?)应用程序中,它将是最常见的'delete'形式。 – 2012-01-11 14:19:57

+0

@JamesKanze为什么? '删除这个'不是正常的释放对象的方法。 – 2012-01-11 14:34:19

+0

@VJovic这取决于应用程序。在很多应用程序中,最常见的(甚至是唯一的)'delete's将会是'delete this'。在其他情况下,'delete'将系统地位于交易管理器中,或类似的东西。而在其他...这取决于你为什么使用动态内存。如果是模型对象,那么'删除这个'或一个事务管理器是最常见的。如果是复杂的数据结构,那么结构的所有者将执行'delete',并且'delete this'几乎不会发生。 – 2012-01-11 15:43:11

0

在这两种情况下,你的堆将被损坏。当然,如果左值(在本例中为“a”)不是指针,选项2中的正确形式为a.done(),那么不能在选项2“ - >”中使用。 但是,是的,如果您尝试删除1)已被删除的内容或2)本地变量,您应该得到一个堆损坏。

调用“删除这个”在技术上是有效的,并释放内存。

编辑我很好奇,实际上尝试这样:

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this; } 
    void test() { x = 2; } 
    int getx() { return x; } 
private: 
    int x; 
} ; 


int main(int argc, char* argv[]) 
{ 
    foo *a = new foo(); 
    a->done(); 
    a->test(); 
    int x = a->getx(); 
    printf("%i\n", x); 
    return x; 
} 

printf的调用会成功,x将持有的价值2

+0

谢谢 - 实际上option2中的“ - >”用法是由外部编辑造成的拼写错误 - 我再次更新了问题。 – NirMH 2012-01-11 14:01:15

+0

因此,'删除这个'会从堆中释放对象内存? – NirMH 2012-01-11 14:01:36

+0

@NirMH:“ - >”用法是你写的。 – duedl0r 2012-01-11 14:04:24

0

我会非常谨慎的关于你释放内存的上下文。虽然称“删除此;”将尝试释放与类结构关联的内存,该操作无法推断原始内存分配的上下文,即,是否从堆或堆栈中分配。我的建议是重写你的代码,以便从外部上下文中调用解除分配操作。请注意,这与释放类内部的结构不同,在这种情况下,请使用析构函数。

3

这个问题已经回答了,但我会添加一个新点,如果你的类确实调用delete this,那么你也应该使析构函数保密。

这确保只有类可以删除自己。

如果您在上面隐藏析构函数,那么您的两个代码示例都将无法编译。

+0

当然它并不能防止所有的错误,例如在调用done()之后使用该对象(即使再次调用done()也会调用双重删除)。 – CashCow 2012-01-11 15:11:45

相关问题