对象数组上的析构函数,这些对象已经被分解

时间:2019-03-22 12:56:14

标签: c++ dynamic-memory-allocation

我对C ++中的Objects有点陌生,并且遇到以下简化问题:

我想创建一个对象的数组,这些对象已由该类的构造方法初始化。

因此:

int main() {

 Test A(1);
 Test B(2);
 Test C(3);
 Test TestArray[3]={A,B,C};

 /*
  Code that both uses A,B,C directly and TestArray
 */

 return 0;
}

重要的是,类Test动态分配其值。因此,析构函数应删除此分配的内存。

因此:

class Test {
    int *PointerToDynMem;
public:
    Test(int);
    ~Test();
};

Test::Test(int a){
    PointerToDynMem=new int(a);
}

Test::~Test(){
    delete PointerToDynMem;
}

我认为,当程序结束ABC超出范围并调用析构函数时,会发生什么。但是似乎TestArray超出范围时,它也称为析构函数,但是ABC已经被释放。错误。

我总是用普通类型的对象(如整数)进行这样的编码,在这里它从未给我带来任何问题。似乎我需要更改某些内容,但不知道具体如何做,因为我想分别给我们两个对象并拥有它们的数组。

让我感到困惑的是,为什么Array应该调用该析构函数,因为它基本上是指向第一个元素的指针,因此并不是真正超出范围的对象。

3 个答案:

答案 0 :(得分:4)

TestArray[3]={A,B,C};将使用默认的复制构造函数复制A,B和C持有的int*。要解决此问题,请创建所有为您静默创建的构造函数和运算符。阅读the rule of three/five/zero

class Test {
    int *PointerToDynMem;
public:
    Test(int);
    Test(const Test&);            // implement this
    Test(Test&&);                 // implement this
    Test& operator=(const Test&); // implement this
    Test& operator=(Test&&);      // implement this
    ~Test();
};

答案 1 :(得分:2)

  

让我感到困惑的是,为什么数组应该调用该析构函数,因为它基本上是指向第一个元素的指针,因此并不是一个超出范围的对象。

一个数组不是一个指针。如果是这样,我们将只有指针。数组名称可以并且确实会衰减到指向数组中第一个元素的指针(这确实很烦人),但它不是指针。该对象将N个对象存储在连续的内存中,并且大小信息是其类型的一部分。这意味着int[5]int[6]的类型不同。

这里发生的是

Test TestArray[3]={A,B,C};

创建一个由三个Test组成的数组,然后从初始化程序Test复制数组中的每个{A,B,C}对象。因此,main结束时,TestArray被销毁,请为每个元素调用析构函数。然后CBA被破坏为该顺序。

这对您来说是个问题,因为您的类仅使用默认的复制构造函数。这意味着数组中的每个对象都有其初始化对象的点的副本,并且它指向相同的内存。因此,当销毁数组时,将在所有成员指针上调用delete,然后CBA尝试再次删除相同的内存,这是未定义的行为。

您需要做的是遵循rule rule of three并创建特殊的成员函数,以便正确地复制您的类,或者您可以使用智能指针/ std::vector形式的RAII并让它们处理所有这些都可以为您服务,您可以使用rule of zero

答案 2 :(得分:1)

对于该代码,您需要添加一个复制构造函数(对于“真实”代码,还需要添加operator =等)

Test::Test(const Test & t) {
  PointerToDynMem=new int(*t.PointerToDynMem);
}

否则您的int*是共享的(TestArray[0].PointerToDynMemA.PointerToDynMem,而TestArray[1].PointerToDynMemB.PointerToDynMem,而TestArray[2].PointerToDynMemC.PointerToDynMem),并且删除了2次