关于c ++:double free或corruption(如果重新排序行,则运行正常)


double free or corruption (runs ok if reorder lines)

本问题已经有最佳答案,请猛点这里访问。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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行,例如:

1
2
Test t;
vector<Test> q;

然后运行正常。为什么会这样?

在Xubuntu 12.04 G++4.6.3上测试。

更新:

这不是重复的问题。我知道需要一个复制构造函数和一个赋值运算符(上面的链接中已经回答了它,示例代码来自那里)。但是,使用int *queue,与原来的链接一样,但是交换1号线和2号线,仍然有错误。仅使用char *vector以及交换1号线和2号线不会导致错误。我的问题是为什么这是一个特殊的案例?有人能在你的平台上查看吗?


您的类型管理资源(动态分配的数组),但不实现三个规则。当你这样做的时候:

1
q.push_back(t);

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

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

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


push指向向量时,C++自动生成EDOCX1×8对象的浅拷贝。当向量超出作用域被破坏时,myArray指针为delete[]d,当测试超出作用域时,同一指针又为delete[]d。

您应该指定一个复制构造函数并对对象进行深度复制。以及一个新的数组。

此外,还强烈建议使用重载赋值运算符(在与上面相同的链接中进行解释)。


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


规则三(http://en.wikipedia.org/wiki/rule_of_3_28c%2b%2b_programming%29)。很可能在两个Test对象之间没有看到正在进行的分配,因此一个对象的旧指针被天真地分配给新对象。


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

push_back复制参数,由于您没有提供复制构造函数,所以有一个默认的复制构造函数,生成一个浅显的复制(只复制指针,不复制内容*)

因此,您需要定义

1
2
3
4
5
6
7
8
Test( const Test& other )
{
    // ...
}
Test& operator=( const Test& other ) // just in case
{
    // ...
}

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

*)导致双重删除——一次从EDOCX1的析构函数中删除,一次从EDOCX1的析构函数中删除(调用向量中所有元素的析构函数)。