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

时间:2014-12-08 01:41:53

标签: c++ constructor

我是学生,所以我为没有使用正确的论坛协议而道歉。我已经在这个问题上找了好几个小时,我的同学都没有人帮忙。我的任务是创建一个复制构造函数,重载赋值运算符(=)和一个析构函数('三大')在C ++中管理堆上的数组。我在VS13下面写的产生了正确的输出,但是我得到了一个调试错误:HEAP CORRUPTION DETECTED: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.
}

2 个答案:

答案 0 :(得分:2)

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

arrayPointer = new double(source.array_capacity);

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

arrayPointer = new double[source.array_capacity];

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

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

答案 1 :(得分:1)

Obvlious队长已经在你的拷贝构造函数中指出了这个问题。

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

代码重复很糟糕,因为它会导致像这样悄悄进入的细微错误。你可以在这里使用的一个好模式就是所谓的copy and swap idiom

要点是您定义了复制构造函数,并且还定义了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而非参考来进一步优化,如链接页面所述。