已被破坏的对象数组上的析构函数

问题描述 投票:1回答:3

我对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应该调用该析构函数,因为它基本上是指向第一个元素的指针,因此实际上并不是一个超出范围的对象。

c++ dynamic-memory-allocation
3个回答
2
投票

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

数组不是指针。如果是的话,我们就会有指针。数组名称可以并且确实衰减到指向数组中第一个元素的指针(这可能非常烦人),但它不是指针。它是一个将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


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
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.