让我们谈谈在 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;
}
这里重要的部分是我们将数据从另一个数组复制到这个对象的数组中,保持每个对象的内存分开。我们还检查了自我分配;否则,我们会从自己复制到自己,这可能会引发错误(不确定它应该做什么)。如果我们要删除并分配更多内存,自分配检查会阻止我们删除需要从中复制的内存。