昨天我学到了一个非常宝贵的教训:遵循三条规则。
我想我会更容易学习,但错误只出现在删除声明中。这是场景:
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的答案总结了一下,但是所有评论中的回答也帮助我弄清楚到底发生了什么,他们坚持为我回答很多问题。如果可以,我会接受两个......
答案 0 :(得分:5)
[注意:newFoo
不再是原始帖子的一部分,它指的是被推入向量fooVec
的对象,现在已在现场完成:foovec.push_back( Foo(Y) )
]
它does
双删除。首先,当newFoo
超出范围时,它会delete[] x
(这是第一次删除)。您在此处撰写了forget to delete Y
,但Y
实际上已被newFoo
的摘要删除。
第二次删除是指newFoo
的副本被删除(fooVec
超出范围时)。 newFoo的副本也是delete[] x
,由于您没有复制构造函数,x
和newFoo
的副本中的newFoo
相同,因此它是双重删除。
现在,您无法轻松解决此问题。因为在您要编写的复制构造函数中,您不知道如何复制x
(它有多少个元素?1?100000?)。
答案 1 :(得分:3)
我不确定你的意思是“这是按预期工作的”
评论。代码中没有内存泄漏,而是
双删除相同的内存。在范围的最后
它被定义,newFoo
的析构函数被调用;这个
反过来删除Y = new int[10000]
分配的内存,
并使Foo
中fooVec
对象中的指针无效。
你的未定义行为从那一刻开始,因为fooVec
现在包含一个正式无法复制的对象。后
任何事情都可能发生:undefined意味着那个,未定义。
您继续访问内存(未定义的行为),以及
在对象的析构函数中第二次删除它
fooVec
(未定义的行为)。
答案 2 :(得分:2)
如果没有显式的复制构造函数,则复制X中的指针值。以下是发生在你身上的事情: