C++ queue::push 后双重释放或损坏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14063791/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Double free or corruption after queue::push
提问by Mihai Neacsu
#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);
}
After I run this, I get a runtime error "double free or corruption". If I get rid of the destructor content (the delete
) it works fine. What's wrong?
运行此程序后,出现运行时错误“双重释放或损坏”。如果我摆脱了析构函数内容 (the delete
),它就可以正常工作。怎么了?
回答by derekerdmann
Let's talk about copying objects in C++.
让我们来谈谈在 C++ 中复制对象。
Test t;
, calls the default constructor, which allocates a new array of integers. This is fine, and your expected behavior.
Test t;
, 调用默认构造函数,它分配一个新的整数数组。这很好,也是您预期的行为。
Trouble comes when you push t
into your queue using q.push(t)
. If you're familiar with Java, C#, or almost any other object-oriented language, you might expect the object you created earler to be added to the queue, but C++ doesn't work that way.
当您t
使用q.push(t)
. 如果您熟悉 Java、C# 或几乎任何其他面向对象的语言,您可能希望将您之前创建的对象添加到队列中,但 C++ 不会那样工作。
When we take a look at std::queue::push
method, we see that the element that gets added to the queue is "initialized to a copy of x." It's actually a brand new object that uses the copy constructor to duplicate every member of your original Test
object to make a new Test
.
当我们查看std::queue::push
method 时,我们看到添加到队列中的元素被“初始化为 x 的副本”。它实际上是一个全新的对象,它使用复制构造函数复制原始Test
对象的每个成员以创建一个新的Test
.
Your C++ compiler generates a copy constructor for you by default! That's pretty handy, but causes problems with pointer members. In your example, remember that int *myArray
is just a memory address; when the value of myArray
is copied from the old object to the new one, you'll now have two objects pointing to the same array in memory. This isn't intrinsically bad, but the destructor will then try to delete the same array twice, hence the "double free or corruption" runtime error.
默认情况下,您的 C++ 编译器会为您生成一个复制构造函数!这很方便,但会导致指针成员出现问题。在您的示例中,请记住这int *myArray
只是一个内存地址;当 的值myArray
从旧对象复制到新对象时,您现在将有两个对象指向内存中的同一个数组。这本质上并不是坏事,但是析构函数会尝试两次删除同一个数组,因此会出现“双重释放或损坏”运行时错误。
How do I fix it?
我如何解决它?
The first step is to implement a copy constructor, which can safely copy the data from one object to another. For simplicity, it could look something like this:
第一步是实现一个复制构造函数,它可以安全地将数据从一个对象复制到另一个对象。为简单起见,它可能如下所示:
Test(const Test& other){
myArray = new int[10];
memcpy( myArray, other.myArray, 10 );
}
Now when you're copying Test objects, a new array will be allocated for the new object, and the values of the array will be copied as well.
现在,当您复制 Test 对象时,将为新对象分配一个新数组,并且该数组的值也将被复制。
We're not completely out trouble yet, though. There's another method that the compiler generates for you that could lead to similar problems - assignment. The difference is that with assignment, we already have an existing object whose memory needs to be managed appropriately. Here's a basic assignment operator implementation:
不过,我们还没有完全解决问题。编译器为您生成的另一种方法可能会导致类似的问题 - 赋值。不同之处在于,通过赋值,我们已经有一个现有的对象,需要对其内存进行适当的管理。这是一个基本的赋值运算符实现:
Test& operator= (const Test& other){
if (this != &other) {
memcpy( myArray, other.myArray, 10 );
}
return *this;
}
The important part here is that we're copying the data from the other array into this object's array, keeping each object's memory separate. We also have a check for self-assignment; otherwise, we'd be copying from ourselves to ourselves, which may throw an error (not sure what it's supposed to do). If we were deleting and allocating more memory, the self-assignment check prevents us from deleting memory from which we need to copy.
这里的重要部分是我们将数据从另一个数组复制到这个对象的数组中,保持每个对象的内存分开。我们也有自我分配的检查;否则,我们会从自己复制到自己,这可能会引发错误(不确定它应该做什么)。如果我们要删除和分配更多内存,自分配检查会阻止我们删除需要从中复制的内存。
回答by Martin York
The problem is that your class contains a managed RAW pointer but does not implement the rule of three (five in C++11). As a result you are getting (expectedly) a double delete because of copying.
问题是您的类包含一个托管的 RAW 指针,但没有实现三的规则(C++11 中的五个)。结果,由于复制,您(预计)会得到双重删除。
If you are learning you should learn how to implement the rule of three (five). But that is not the correct solution to this problem. You should be using standard container objects rather than try to manage your own internal container. The exact container will depend on what you are trying to do but std::vector is a good default (and you can change afterwords if it is not opimal).
如果您正在学习,您应该学习如何实施三(五)规则。但这不是解决这个问题的正确方法。您应该使用标准容器对象,而不是尝试管理自己的内部容器。确切的容器将取决于您要尝试做什么,但 std::vector 是一个很好的默认值(如果它不是最理想的,您可以更改后记)。
#include <queue>
#include <vector>
class Test{
std::vector<int> myArray;
public:
Test(): myArray(10){
}
};
int main(){
queue<Test> q
Test t;
q.push(t);
}
The reason you should use a standard container is the separation of concerns
. Your class should be concerned with either business logic or resource management (not both). Assuming Test
is some class you are using to maintain some state about your program then it is business logic and it should not be doing resource management. If on the other hand Test
is supposed to manage an array then you probably need to learn more about what is available inside the standard library.
您应该使用标准容器的原因是separation of concerns
. 您的课程应该关注业务逻辑或资源管理(而不是两者)。假设Test
您使用某个类来维护有关程序的某些状态,那么它是业务逻辑,不应该进行资源管理。另一方面,如果Test
应该管理一个数组,那么您可能需要了解更多有关标准库中可用内容的信息。
回答by Astro - Amit
You are getting double free or corruptionbecause first destructor is for object qin this case the memory allocated by newwill be free.Next time when detructor will be called for object tat that time the memory is already free (done for q) hence when in destructor delete[] myArray;will execute it will throw double free or corruption. The reason is that both object sharing the same memory so define \copy, assignment, and equal operator as mentioned in above answer.
您正在获得双重释放或损坏,因为第一个析构函数用于对象q,在这种情况下,new分配的内存将是空闲的。下次当对象t调用析构函数时,内存已经空闲(为 q 完成)因此当在析构函数中时delete[] myArray; 将执行它会抛出double free 或 corruption。原因是两个对象共享相同的内存,因此定义 \copy、assignment 和 equal 运算符,如上述答案所述。
回答by Ryan Guthrie
You need to define a copy constructor, assignment, operator.
您需要定义一个复制构造函数、赋值、运算符。
class Test {
Test(const Test &that); //Copy constructor
Test& operator= (const Test &rhs); //assignment operator
}
Your copy that is pushed on the queue is pointing to the same memory your original is. When the first is destructed, it deletes the memory. The second destructs and tries to delete the same memory.
推送到队列中的副本指向与原始内存相同的内存。当第一个被破坏时,它会删除内存。第二个破坏并尝试删除相同的内存。
回答by tolgayilmaz
You can also try to check null before delete such that
您也可以尝试在删除之前检查空值,以便
if(myArray) { delete[] myArray; myArray = NULL; }
or you can define all delete operations ina safe manner like this:
或者您可以像这样以安全的方式定义所有删除操作:
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p) { delete (p); (p) = NULL; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p) = NULL; } }
#endif
and then use
然后使用
SAFE_DELETE_ARRAY(myArray);
回答by user888379
Um, shouldn't the destructor be calling delete, rather than delete[]?
嗯,析构函数不应该调用delete,而不是delete[]?