我们来谈谈C++中的复制对象。
Test t;
,调用默认构造函数,该构造函数分配一个新的整数数组。这很好,也是您预期的行为。
当你推的时候麻烦就来了t
使用进入您的队列q.push(t)
。如果您熟悉 Java、C# 或几乎任何其他面向对象的语言,您可能希望将之前创建的对象添加到队列中,但 C++ 不会这样工作。
当我们看一看std::queue::push method,我们看到添加到队列中的元素“初始化为 x 的副本”。它实际上是一个全新的对象,它使用复制构造函数来复制原始对象的每个成员Test
对象创建一个新的Test
.
默认情况下,您的 C++ 编译器会生成一个复制构造函数!这非常方便,但会导致指针成员出现问题。在你的例子中,请记住int *myArray
只是一个内存地址;当值myArray
从旧对象复制到新对象后,您现在将有两个对象指向内存中的同一数组。这本质上并不是坏事,但是析构函数会尝试两次删除同一个数组,因此会出现“双重释放或损坏”运行时错误。
我如何解决它?
第一步是实施复制构造函数,它可以安全地将数据从一个对象复制到另一个对象。为简单起见,它可能看起来像这样:
Test(const Test& other){
myArray = new int[10];
memcpy( myArray, other.myArray, 10 );
}
现在,当您复制 Test 对象时,将为新对象分配一个新数组,并且该数组的值也将被复制。
不过,我们还没有完全摆脱麻烦。编译器为您生成的另一种方法可能会导致类似的问题 - 赋值。不同之处在于,通过赋值,我们已经有了一个现有的对象,需要对其内存进行适当的管理。这是一个基本的赋值运算符实现:
Test& operator= (const Test& other){
if (this != &other) {
memcpy( myArray, other.myArray, 10 );
}
return *this;
}
这里重要的部分是我们将数据从另一个数组复制到该对象的数组中,保持每个对象的内存独立。我们还有一个自我分配的检查;否则,我们将从我们自己复制到我们自己,这可能会引发错误(不确定它应该做什么)。如果我们要删除并分配更多内存,自分配检查会阻止我们删除需要复制的内存。