C++ 这是为什么;“三原则”;失败真的失败了吗?
昨天我学到了一个极其宝贵的教训:遵循三个法则 我想我会更容易理解,但错误只出现在delete语句上。情况如下:C++ 这是为什么;“三原则”;失败真的失败了吗?,c++,C++,昨天我学到了一个极其宝贵的教训:遵循三个法则 我想我会更容易理解,但错误只出现在delete语句上。情况如下: foo.h class foo{ public: ... foo(int *Y); ~foo(); int *X; } foo.cpp ... (.. constructor sets X to Y..) foo:~foo(){ delete
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
foo.h
福班{
公众:
...
foo(int*Y);
~foo();
int*X;
}
foo.cpp
...
(…构造函数将X设置为Y…)
foo:~foo(){
删除[]X;
}
main.cpp
矢量fooVec;
{//这只是为了更容易地演示范围问题。
Y=新整数[10000];
(…初始Y…)
push(Foo(Y))//我明白了:这调用了复制构造函数,违反了三的规则
(…忘记删除Y…)
}
//Y现在超出范围,因此这是内存泄漏
cout[注意:newFoo
不再是原始贴子的一部分,它指的是被推入向量fooVec
的对象,现在在原处完成:fooVec.push_back(Foo(Y))
]
它执行
双重删除。首先,当newFoo
超出范围时,它会delete[]x
(这是第一次删除)。您在此处编写了忘记删除Y
,但Y
实际上已被newFoo
的描述者删除
第二次删除是当newFoo
的副本被删除时(当fooVec
超出范围时)。newFoo的副本也会删除[]x
,因为您没有副本构造函数,x
在newFoo
和newFoo
的副本中是相同的,因此它是双重删除
现在,您将无法轻松解决此问题。因为在您将要编写的复制构造函数中,您不知道如何复制
x
(它有多少个元素?1?100000?) 我不知道你所说的“这一切都如愿”是什么意思
评论。代码中没有内存泄漏,而是
对同一内存的双重删除。在中范围的末尾
定义后,调用newFoo
的析构函数;这
依次删除在Y=new int[10000]
处分配的内存,
并使fooVec
中的Foo
对象中的指针无效。
您未定义的行为从那一刻开始,因为fooVec
现在包含一个形式上不可复制的对象。之后
任何事情都可能发生:未定义的意思就是,未定义
您继续访问内存(未定义的行为),然后
在中对象的析构函数中再次删除它
fooVec
(未定义的行为) 如果没有显式复制构造函数,将复制X中的指针值。以下是发生在你身上的事情:
- 实例化保存指针值Y的newFoo
- 将newfoo推到vector上,vector现在保存另一个foo,它也有一个指针值Y
- newFoo超出范围,析构函数删除值Y指向的内存
- cout使用指针值Y,但由于已删除的内存尚未被覆盖,因此它“起作用”
- fooVec超出范围,它所持有的foo的析构函数试图删除Y指向的内存
- 砰
语句不能按预期工作呢?我编辑了我的代码。我从来没有明确创建过newfOO:不确定这是否会改变什么。当你释放内存时,它不会被破坏。你只是说这里的这段内存现在可以用来做其他事情了
。访问被释放的内存可能会起作用,也可能会对俄罗斯发动核攻击。换句话说,这是未定义的行为@rubenvb在delete[]x
之后,x不为空。即使在析构函数中将其设置为NULL也无济于事,因为newFoo
的副本仍将具有原始指针值,并将尝试删除它。@Tommy这样做会更安全,但代价会更高。核心C++哲学是“你只为你所用的东西付费”。你可以创建一个智能指针,当它释放内存时,它会使内存失效。或者是一个保护访问的内存池。或者别的什么。但为什么正确的程序要为捕获坏程序中的错误而付出性能上的代价呢?我现在应该使用vector了。然而,过去我一直避免了解三的规则,因为我总是使用vector,我认为它默认带有一个深度复制构造函数。因此,在这里使用动态分配的数组(大小不是静态的,而是在运行时读取)实际上让我了解了vector为我处理的一些事情。“按预期工作”意味着打印确实在Y中初始化的值。@Tommy这是未定义行为的一个可能结果。当然,这不是唯一的一个,在不同的上下文中,您的代码可能会做不同的事情。newFoo应该更改为Foo(Y)。对此很抱歉,但我从未实际实例化newFoo,我只是在push_-back语句中隐式地进行了实例化。再次抱歉之后更改了我的问题。@Tommy您的编辑并没有真正改变我的答案,只需将newfoo
替换为调用pushback()时创建的临时foo
。临时对象超出范围并删除Y指向的内存。还要注意的是,向量在其存储耗尽时将对其所有对象执行额外的复制构造函数/析构函数调用。所以,即使你泄漏了所有的物体