复制构造函数和重载赋值运算符的堆损坏错误

Heap Corruption error with Copy constructor and overloaded assignment operator

本文关键字:损坏 错误 赋值运算符 构造函数 重载 复制      更新时间:2023-10-16

我是一名学生,所以我为没有使用正确的论坛协议而预先道歉。我在这个问题上搜索了几个小时,我的同学都帮不上忙。我的任务是创建一个复制构造函数、重载赋值运算符 (=( 和一个析构函数("三大"(,C++来管理堆上的数组。 我在VS13中下面写的内容会产生正确的输出,但是我收到调试错误: 检测到堆损坏:C++ CRT 检测到应用程序在堆缓冲区结束后写入内存谁能给我一些指导,我什至不知道去哪里看。谢谢!!

//copy constructor
myList::myList(const myList& source){
cout << "Invoking copy constructor." << endl;
array_capacity = source.array_capacity; //shallow copy
elements = source.elements; //shallow copy
delete[] arrayPointer;
arrayPointer = new double(source.array_capacity); //deep copy
for (int i = 0; i < array_capacity; i++) //copy array contents
    {
        arrayPointer[i] = source.arrayPointer[i];
    }
}
//overloaded assignment operator
myList& myList::operator=(const myList& source){
cout << "Invoking overloaded assignment." << endl;
if (this != &source){
array_capacity = source.array_capacity; //shallow copy
elements = source.elements; //shallow copy
delete[] arrayPointer; //delete original array from heap
arrayPointer = new double(array_capacity); //deep copy
for (int i = 0; i < source.array_capacity; i++) {//copy array contents
    arrayPointer[i] = source.arrayPointer[i];
        }
    }
return *this;
}
//destructor
myList::~myList(){ 
cout << "Destructor invoked."<< endl;
delete[] arrayPointer;  // When done, free memory pointed to by myPointer.
arrayPointer = NULL;    // Clear myPointer to prevent using invalid memory reference.
}

你的代码有几个问题。首先,您在arrayPointer上调用delete,但它尚未初始化为任何内容。实际上,这最终可能会删除您已经分配的内存,或者导致delete实现中的超出或资产。其次,当您初始化它时,您正在分配一个初始化为 source.array_capacity 值的单个double。请注意下面行中使用的括号。

arrayPointer = new double(source.array_capacity);

这肯定会在复制过程中导致未定义的行为,因为您最终会访问数组边界之外的元素。上面的行存在于复制构造函数和复制赋值运算符中,应该使用方括号,如下所示:

arrayPointer = new double[source.array_capacity];

您也从不检查是否有任何元素存储在 myListsource实例中。在这种情况下,您可能应该将nullptr(或 C++03 中的NULL(分配给arrayPointer

作为旁注,您实际上不需要将NULL分配给析构函数中的arrayPointer。一旦对象被销毁,它就消失了,任何事后访问它的尝试都会导致未定义的行为。

Obvlious 船长已经指出了复制构造函数中的问题。

您会注意到,复制构造函数和赋值运算符包含许多相同的代码,但存在细微差别。事实上,这可能是你最终出错的方式(赋值运算符需要delete[]旧值,但复制构造函数不需要。

代码重复是不好的,因为它会导致像这样的微妙错误蔓延。您可以在此处使用的一个很好的模式是所谓的复制和交换习惯用法。

要点是你定义了复制构造函数,你也定义了swap .然后您可以免费获得作业。 这是有效的swap因为它比赋值运算符更容易正确实现,另一个主要好处是不会出错(您不必担心内存不足错误等(。

在你的代码中;保留你的固定复制构造函数,你可以在类定义中添加:

friend void swap( myList &a, myList &b )
{
    std::swap( a.array_capacity, b.array_capacity );
    std::swap( a.arrayPointer, b.arrayPointer );
    std::swap( a.elements, b.elements );
}

现在赋值运算符非常简单:

myList& myList::operator=(const myList &source)
{
    myList temp(source);
    swap(*this, temp);
    return *this;
}

当前对象的旧资源由 temp 的析构函数删除。此版本甚至不需要检查自我分配std::swap(x, x)因为它是明确定义的。

这甚至可以通过按值而不是按引用source来进一步优化,如链接页面上所述。