我对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;
}
我认为,当程序结束A
,B
和C
超出范围并调用析构函数时,会发生什么。但是似乎TestArray
超出范围时,它也称为析构函数,但是A
,B
,C
已经被释放。错误。
我总是用普通类型的对象(如整数)进行这样的编码,在这里它从未给我带来任何问题。似乎我需要更改某些内容,但不知道具体如何做,因为我想分别给我们两个对象并拥有它们的数组。
让我感到困惑的是,为什么Array应该调用该析构函数,因为它基本上是指向第一个元素的指针,因此并不是真正超出范围的对象。
答案 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
被销毁,请为每个元素调用析构函数。然后C
,B
和A
被破坏为该顺序。
这对您来说是个问题,因为您的类仅使用默认的复制构造函数。这意味着数组中的每个对象都有其初始化对象的点的副本,并且它指向相同的内存。因此,当销毁数组时,将在所有成员指针上调用delete
,然后C
,B
和A
尝试再次删除相同的内存,这是未定义的行为。
您需要做的是遵循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].PointerToDynMem
是A.PointerToDynMem
,而TestArray[1].PointerToDynMem
是B.PointerToDynMem
,而TestArray[2].PointerToDynMem
是C.PointerToDynMem
),并且删除了2次