在对项目进行了大量更改后,我创建了一个错误,让我花了很长时间才能跟踪。
我有一个包含动态分配数组的类。然后我创建了这个类的动态数组。然后我可以删除[]那个数组。但是,如果我在删除它之前替换数组中的项目,则会导致错误。在调试模式下,它从dbgdel.cpp“Expression:_BLOCK_TYPE_IS_VALID(pHead-> nBlockUse)”中给出断言消息。这是一个小程序来演示。
class SomeClass {
public:
int *data;
SomeClass() {
data = nullptr;
}
SomeClass(int num) {
data = new int[num];
}
~SomeClass() {
if (data != nullptr) {
delete[] data;
}
}
};
int main(int argc, char *args[]) {
SomeClass *someArray = new SomeClass[10];
//If you comment this out, there is no error. Error gets thrown when deleting
someArray[0] = SomeClass(10);
delete[] someArray;
return 0;
}
我很好奇,为什么会这样?当替换数组中的项时,将调用其析构函数。然后,新项目将数据分配在与数组不同的位置。然后delete []调用数组中所有对象的析构函数。当析构函数被调用时,它们应该删除项目的数据数组。我无法想象问题是什么,但我想是否有人可以解释。
答案 0 :(得分:9)
您的类已损坏:它有一个非平凡的析构函数,但您没有定义复制构造函数和复制赋值运算符。这意味着无法正确复制或分配类(因为不会复制或分配适当的可破坏状态),正如您在示例代码中注意到的那样。
你可以让你的班级不可用(在这种情况下你的代码不再编译),或者仅移动,在这种情况下你需要通过实施数据的深层副本来定义移动构造和移动分配,或者可以正确地复制。
在这里&#39>如何添加以下定义:
<强>不可复制强>
SomeClass(SomeClass const &) = delete;
SomeClass & operator(SomeClass const &) = delete;
<强>可移动仅强>
SomeClass(SomeClass const &) = delete;
SomeClass(SomeClass && rhs) : data(rhs.data) { rhs.data = nullptr; }
SomeClass & operator(SomeClass const &) = delete;
SomeClass & operator(SomeClass && rhs) {
if (this != &rhs) { delete data; data = rhs.data; rhs.data = nullptr; }
return *this;
}
<强>能够复制强>
SomeClass(SomeClass const & rhs) : ptr(new int[rhs->GetSizeMagically()]) {
/* copy from rhs.data to data */
}
SomeClass & operator(SomeClass const & rhs) {
if (this == &rhs) return *this;
int * tmp = new int[rhs->GetSizeMagically()];
/* copy data */
delete data;
data = tmp;
}
// move operations as above
结果是析构函数的性质决定了类的不变量,因为每个对象必须始终是可破坏的。从中可以推断出复制和移动操作所需的语义。 (这通常被称为三法则或五法则。)
答案 1 :(得分:5)
Kerrek SB的答案很棒。我只想澄清一下,在你的代码中,内存被释放了两次。
此代码
someArray[0] = SomeClass(10);
与此相同
SomeClass temp(10);
someArray[0] = temp; //here temp.data is copied to someArray[0].data
然后〜为SomeClass()调用temp,第一次释放data
。
下面
delete[] someArray;
〜为someArray [0]调用SomeClass(),第二次释放data
。