当我有一个类的std :: vector时,我对如何使用析构函数感到困惑。
所以如果我按如下方式创建一个简单的类:
class Test
{
private:
int *big;
public:
Test ()
{
big = new int[10000];
}
~Test ()
{
delete [] big;
}
};
然后在我的主要功能中,我执行以下操作:
Test tObj = Test();
vector<Test> tVec;
tVec.push_back(tObj);
当我超出范围时,我在Test的析构函数中遇到运行时崩溃。为什么这样,我怎样才能安全地释放我的记忆?
答案 0 :(得分:21)
问题是您没有为Test
定义复制构造函数。因此,编译器会为您生成一个默认的复制构造函数,它只复制对象的内容 - 在本例中为int指针。
现在,当您将对象推回到向量中时,会使用复制构造函数隐式复制它。这导致两个对象指向相同的int数组!所以最后,两个析构函数尝试删除相同的数组--BANG。
每当你定义一个通过指针*拥有成员的类时,除了析构函数之外,必须还为它定义一个复制构造函数。 更新:和一个赋值运算符,出于同样的原因(感谢@James: - )
Update2:解决所有这些限制的一个简单方法是定义静态数组而不是动态分配的数组:
class Test
{
private:
int big[10000];
// no need for constructors, destructor or assignment operator
};
但是,最佳做法是使用std::vector<int>
而不是数组。
* 即包含指向拥有所有权语义的成员的指针(感谢@Steve Jessop澄清)
答案 1 :(得分:15)
你的问题在这里:
Test tObj = Test();
Test()
创建一个临时Test
对象,然后将其复制到tObj
。此时,tObj
和临时对象都将big
设置为指向数组。然后临时对象被破坏,它会调用析构函数并销毁数组。因此当tObj
被破坏时,它会再次尝试销毁已经被破坏的数组。
此外,当tVec
被销毁时,它会破坏其元素,因此已经被破坏的数组将再次被销毁。
您应该定义一个复制构造函数和赋值运算符,以便在复制Test
对象时,big
数组被复制,或者具有某种引用计数,以便它不会被获取在所有业主被摧毁之前被摧毁。
一个简单的解决方法是像这样定义你的类:
class Test
{
private:
std::vector<int> big;
public:
Test (): big(10000) {}
};
在这种情况下,您不需要定义任何析构函数,复制构造函数或赋值运算符,因为std::vector<>
成员将处理所有事情。 (但请注意,这意味着只要复制Test
的实例,就会分配和复制10,000个字节。)
答案 2 :(得分:0)
如果没有复制构造函数,向量将创建对象的平面副本。这导致两个类型为Test
的对象引用相同的数组big
。第一个实例在被销毁时删除该数组,然后第二个实例尝试取消引用已删除的指针,这是未定义的行为。
答案 3 :(得分:0)
Test tObj = Test();
这是错误的,因为它不会创建副本:
Test tObj;
这也创造了很多副本:
vector<Test> tVec;
tVec.push_back(tObj);
因此,如果你释放一个int数组,你将释放所有数组。以下删除将失败。
您需要的是:
使用复制构造函数为每个类创建一个单独的数组
为什么要使用指针?
class Test { private: int big[10000]; public: };
这样可以正常工作。