Mih*_*csu 52 c++ runtime-error delete-operator
#include <queue>
using namespace std;
class Test{
int *myArray;
public:
Test(){
myArray = new int[10];
}
~Test(){
delete[] myArray;
}
};
int main(){
queue<Test> q
Test t;
q.push(t);
}
Run Code Online (Sandbox Code Playgroud)
运行此操作后,我收到运行时错误"double free or corruption".如果我摆脱析构函数内容(delete),它工作正常.怎么了?
der*_*ann 85
Test t;,调用默认构造函数,它分配一个新的整数数组.这很好,你的预期行为.
t使用时进入队列时出现问题q.push(t).如果您熟悉Java,C#或几乎任何其他面向对象的语言,您可能希望将您创建的对象添加到队列中,但C++不会以这种方式工作.
当我们看一下std::queue::push方法时,我们看到添加到队列中的元素被"初始化为x的副本".它实际上是一个全新的对象,它使用复制构造函数复制原始Test对象的每个成员来创建一个新对象Test.
您的C++编译器默认为您生成一个复制构造函数!这非常方便,但会导致指针成员出现问题.在您的示例中,请记住这int *myArray只是一个内存地址; 当值myArray从旧对象复制到新对象时,您现在将有两个对象指向内存中的同一个数组.这本身并不坏,但析构函数将尝试两次删除相同的数组,因此"双重释放或损坏"运行时错误.
第一步是实现一个复制构造函数,它可以安全地将数据从一个对象复制到另一个对象.为简单起见,它看起来像这样:
Test(const Test& other){
myArray = new int[10];
memcpy( myArray, other.myArray, 10 );
}
Run Code Online (Sandbox Code Playgroud)
现在,当您复制Test对象时,将为新对象分配一个新数组,并且还将复制该数组的值.
不过,我们还没有彻底摆脱困境.编译器为您生成的另一种方法可能会导致类似的问题 - 分配.不同之处在于,通过赋值,我们已经有了一个现有对象,其内存需要进行适当的管理.这是一个基本的赋值运算符实现:
Test& operator= (const Test& other){
if (this != &other) {
memcpy( myArray, other.myArray, 10 );
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
这里的重要部分是我们将数据从另一个数组复制到该对象的数组中,保持每个对象的内存分离.我们还检查自我分配; 否则,我们会从自己复制到自己,这可能会引发错误(不确定它应该做什么).如果我们删除并分配更多内存,则自我分配检查会阻止我们删除需要复制的内存.
Mar*_*ork 16
问题是你的类包含一个托管的RAW指针,但没有实现三个规则(C++ 11中的五个).因此,由于复制,您将获得(预期)双重删除.
如果你正在学习,你应该学习如何实施三(五)的规则.但这不是解决这个问题的正确方法.您应该使用标准容器对象而不是尝试管理自己的内部容器.确切的容器将取决于你想要做什么,但std :: vector是一个很好的默认值(如果它不是opimal,你可以改变后缀).
#include <queue>
#include <vector>
class Test{
std::vector<int> myArray;
public:
Test(): myArray(10){
}
};
int main(){
queue<Test> q
Test t;
q.push(t);
}
Run Code Online (Sandbox Code Playgroud)
你应该使用标准容器的原因是separation of concerns.您的课程应该关注业务逻辑或资源管理(而不是两者).假设Test您正在使用一些类来维护程序的某些状态,那么它就是业务逻辑,它不应该进行资源管理.另一方面,如果Test应该管理一个数组,那么您可能需要了解有关标准库中可用内容的更多信息.