我对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应该调用该析构函数,因为它基本上是指向第一个元素的指针,因此实际上并不是一个超出范围的对象。
我很困惑的是为什么Array应该调用该析构函数,因为它基本上是指向第一个元素的指针,因此实际上并不是一个超出范围的对象。
数组不是指针。如果是的话,我们就会有指针。数组名称可以并且确实衰减到指向数组中第一个元素的指针(这可能非常烦人),但它不是指针。它是一个将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
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();
};
对于该代码,您需要添加一个复制构造函数(以及“真实”代码的operator = etc)
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次