为什么这个“三个规则”的失败实际上失败了?

时间:2013-10-22 13:32:14

标签: c++

昨天我学到了一个非常宝贵的教训:遵循三条规则。

我想我会更容易学习,但错误只出现在删除声明中。这是场景:

foo.h
    class foo{
    public:
        ...
        foo(int *Y); 
        ~foo();  
        int *X;
    }
 foo.cpp
     ...
     (.. constructor sets X to Y..)
     foo:~foo(){
         delete [] X;
     }

main.cpp
    vector<Foo> fooVec;
    { // this is just to demonstrate scope problems more easily.  
         Y = new int[10000];
         (...init Y...)
         fooVec.push(Foo(Y)) // I get it: this calls copy constructor, violating rule of three
         (...forget to delete Y...)
    } 
    // Y is now out of scope so this is a memory leak
    cout << fooVec[0].[X][0] << " " <<  fooVec[0].[X][1] // THIS WORKS AS INTENDED DUE TO MEMORY LEAK
    // program ends, fooVec goes out of scope

"pointer being freed has not been allocated"一起轰炸。我追溯到fooVec超出范围的地步,它调用了foos析构函数,它试图删除X.我的主要问题:为什么删除实际上失败了?我从来没有删除代码中的Y(我得到这是一个内存泄漏),所以我实际上并没有删除指针。此外,内存显然存在,因为cout行有效。如果行失败了,我会更早地解决问题。

下面的评论似乎表明“当Foo(Y)超出范围时,X被删除”。但如果是这种情况,为什么地球上的cout声明有用呢?

注意:我没有复制构造函数和赋值重载,因此“三个失败的规则”。由于矢量push_back语句,我得到了我应该拥有的。我问为什么不让他们在这里杀了我,因为我忘了释放Y所以我实际上并没有删除指针两次。

编辑:

谢谢大家的帮助。 user1158692s的答案总结了一下,但是所有评论中的回答也帮助我弄清楚到底发生了什么,他们坚持为我回答很多问题。如果可以,我会接受两个......

3 个答案:

答案 0 :(得分:5)

[注意:newFoo不再是原始帖子的一部分,它指的是被推入向量fooVec的对象,现在已在现场完成:foovec.push_back( Foo(Y) )]

does双删除。首先,当newFoo超出范围时,它会delete[] x(这是第一次删除)。您在此处撰写了forget to delete Y,但Y实际上已被newFoo的摘要删除。

第二次删除是指newFoo的副本被删除(fooVec超出范围时)。 newFoo的副本也是delete[] x,由于您没有复制构造函数,xnewFoo的副本中的newFoo相同,因此它是双重删除。

现在,您无法轻松解决此问题。因为在您要编写的复制构造函数中,您不知道如何复制x(它有多少个元素?1?100000?)。

答案 1 :(得分:3)

我不确定你的意思是“这是按预期工作的” 评论。代码中没有内存泄漏,而是 双删除相同的内存。在范围的最后 它被定义,newFoo的析构函数被调用;这个 反过来删除Y = new int[10000]分配的内存, 并使FoofooVec对象中的指针无效。 你的未定义行为从那一刻开始,因为fooVec 现在包含一个正式无法复制的对象。后 任何事情都可能发生:undefined意味着那个,未定义。

您继续访问内存(未定义的行为),以及 在对象的析构函数中第二次删除它 fooVec(未定义的行为)。

答案 2 :(得分:2)

如果没有显式的复制构造函数,则复制X中的指针值。以下是发生在你身上的事情:

  • 实例化保存指针值Y
  • 的newFoo
  • 将newfoo推送到vector,vector现在拥有另一个foo,也有一个指针值Y
  • newFoo超出范围,析构函数删除值Y指向的内存
  • cout使用指针值Y,但由于已删除的内存尚未被覆盖,因此“工作”
  • fooVec超出范围,它持有的foo的析构函数试图删除Y指向的内存
  • BANG