2

昨天我学到了非常宝贵的一课:遵循三法则。

我想我会更容易学会它,但错误只出现在删除语句上。这是场景:

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说法会奏效呢?

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

编辑:

谢谢大家的帮助。user1158692s answer总结了它,但是thelambs answer中的所有评论也帮助我弄清楚了到底发生了什么,他们坚持为我回答了许多问题。可以的话两个都接受。。

4

3 回答 3

5

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

does双重删除。首先,当newFoo超出范围时,它会这样做delete[] x(这是第一次删除)。你写forget to delete Y在这里,但Y实际上被newFoo.

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

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

于 2013-10-22T13:37:15.973 回答
3

我不确定您所说的“按预期工作”评论是什么意思。您的代码中没有内存泄漏,而是对同一内存进行了双重删除。在定义它的作用域结束时,newFoo调用析构函数;这反过来会删除在 中分配的内存,并使 中的对象中Y = new int[10000]的指针无效。您的未定义行为从那一刻开始,因为 现在包含一个正式不可复制的对象。在那之后,任何事情都可能发生:未定义意味着未定义。FoofooVecfooVec

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

于 2013-10-22T13:42:50.340 回答
2

Without an explicit copy constructor, the pointer value in X is copied. Here's what's happening to you:

  • Instantiate newFoo which holds pointer value Y
  • push newfoo onto vector, vector now holds another foo which also has a pointer value Y
  • newFoo goes out of scope, destructor deletes memory pointed to by value Y
  • cout uses pointer value Y, but as deleted memory has yet to be overwritten it "works"
  • fooVec goes out of scope, the destructor of the foo it holds attempts to delete memory pointed to by Y
  • BANG
于 2013-10-22T13:47:49.340 回答