在队列::推送后双重免费或损坏

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

我们来谈谈用C++复制对象.

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应该管理一个数组,那么您可能需要了解有关标准库中可用内容的更多信息.