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



在链接中使用示例,但更改为使用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 行不会导致错误。我的问题是为什么会出现这种特殊情况?任何人都可以在您的平台上检查它吗?

您的类型管理资源(动态分配的数组),但不实现三规则。执行此操作时:

q.push_back(t);

q复制它拥有的t。因此,现在您有一个对象的两个副本引用相同的数据,并尝试在其中调用delete

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

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

当您将

Test对象push到向量时,C++会自动创建该对象的浅拷贝。当向量超出范围并被破坏时,myArray指针delete[] d。然后,当 Test 超出范围时,将再次delete[]同一指针。

应指定复制构造函数并创建对象的深层副本。以及一个新阵列。

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

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

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

push_back复制参数,并且由于您没有提供复制构造函数,因此有一个默认的,制作一个浅拷贝(只复制指针,没有内容*)

因此,您需要定义一个

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

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

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

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

最新更新