双重免费或损坏(如果重新排序行,则运行正常)

时间:2014-01-03 12:59:29

标签: c++

使用link中的示例,但更改为使用char *vector

#include <vector>
using namespace std;

class Test{
    char *myArray;

public:
    Test(){
        myArray = new char[10];
    }

    ~Test(){
        delete[] myArray;
    }   
};  


int main(){
    vector<Test> q; // line 1
    Test t;         // line 2
    q.push_back(t);
}

会导致双重释放或损坏错误。但是,如果在第1行之前运行第2行,例如:

Test t;
vector<Test> q;

然后运行正常。那是为什么?

在Xubuntu 12.04 g ++ 4.6.3上测试。

更新:

这不是重复的问题。我理解需要一个复制构造函数和赋值运算符(它已在上面的示例代码所在的链接中得到了解答)。但是,在原始链接中使用int *queue,但是交换第1行和第2行仍然存在错误。仅使用char *vector并交换第1行和第2行不会导致错误。我的问题是为什么这个特例?任何人都可以在你的平台上查看它吗?

5 个答案:

答案 0 :(得分:8)

您的类型管理资源(动态分配的数组),但不实现rule of three。当你这样做时:

q.push_back(t);

q制作了它所拥有的t的副本。所以现在你有一个对象的两个副本引用相同的数据,并试图在其中调用delete

您需要实现复制构造函数和赋值运算符。或者使用管理自己资源的类,例如std::stringstd::vector

在已删除的阵列上调用delete[]未定义的行为(UB)。这意味着有时您的程序似乎可以正常工作。您不能依赖具有未定义行为的程序来执行任何操作。交换第1行和第2行会颠倒tq被销毁的顺序。这似乎会在您的平台上产生不同的结果,但两者都是UB。

答案 1 :(得分:2)

当您Test对象push时,C ++会自动生成一份浅层副本。当向量超出范围并被破坏时,myArray指针为delete[] d。然后,当Test超出范围时,同样的指针再次为delete[] d。

您应指定copy constructor并制作对象的深层副本。随着一个新阵列。

强烈建议重载赋值运算符(在上面相同的链接中解释)。

答案 2 :(得分:0)

因为你需要一个复制构造函数。

push_back复制参数,因为你没有提供复制构造函数,所以有一个默认的复制构造函数,做一个浅拷贝(只复制指针,没有内容 *)< / p>

所以,你需要定义一个

Test( const Test& other )
{
    // ...
}
Test& operator=( const Test& other ) // just in case
{
    // ...
}

并进行深层复制,手动复制char*缓冲区。

*)导致双重删除 - 一次来自t的析构函数,一次来自q的析构函数(调用向量中所有元素的析构函数)

答案 3 :(得分:0)

三条规则(http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29)。可能正在进行一项在两个Test对象之间看不到的赋值,因此来自一个对象的旧指针被天真地分配给新对象。

答案 4 :(得分:0)

这是因为当vector由vector管理以及push_back插入Test对象时,会自动创建和删除Test对象。想象一下,当向元素添加新元素时,需要更多空间并分配,并将现有元素复制到新地址。这意味着它们被删除并且它们的动态内存被释放。为了能够克服这个问题,请为您的类定义复制构造函数,以便对该对象进行深层复制。或者使用诸如unique_ptr或shared_ptr之类的智能指针(来自boost或C ++ 11)。