我是学生,所以我为没有使用正确的论坛协议而道歉。我已经在这个问题上找了好几个小时,我的同学都没有人帮忙。我的任务是创建一个复制构造函数,重载赋值运算符(=)和一个析构函数('三大')在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.
}
答案 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
而非参考来进一步优化,如链接页面所述。