使用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行不会导致错误。我的问题是为什么这个特例?任何人都可以在你的平台上查看它吗?
答案 0 :(得分:8)
您的类型管理资源(动态分配的数组),但不实现rule of three。当你这样做时:
q.push_back(t);
q
制作了它所拥有的t
的副本。所以现在你有一个对象的两个副本引用相同的数据,并试图在其中调用delete
。
您需要实现复制构造函数和赋值运算符。或者使用管理自己资源的类,例如std::string
或std::vector
。
在已删除的阵列上调用delete[]
是未定义的行为(UB)。这意味着有时您的程序似乎可以正常工作。您不能依赖具有未定义行为的程序来执行任何操作。交换第1行和第2行会颠倒t
和q
被销毁的顺序。这似乎会在您的平台上产生不同的结果,但两者都是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)。