内存泄漏;删除时引发异常

Memory Leak; when deleted exception raised

本文关键字:异常 删除 泄漏 内存      更新时间:2023-10-16

我正在编写一个可重新调整大小的数组类(std::vector),作为练习,使用手动指针(因为我想在开始使用智能指针之前知道它们是如何工作的)。然而,Valgrind报告checkMax()函数的内存泄漏。

template <typename T>
class Array{
 public:
 Array() : len(0),maxLen(1){
    array=new T[maxLen];
    // ........ 
}
Array(unsigned length, T&content=0) : len(length),maxLen(length*2){
    array=new T[maxLen];
    //..............
}
~Array(){
    //delete[] array;
}
//..............
void push_back(const T& content){
    checkMax();
// do stuff...
}
private:
T* array;
unsigned int len;
unsigned int maxLen;
..
void checkMax(){
    if(len==maxLen){
    //allocate more memory for the array
        maxLen*=2;
        T*temp=new T[maxLen]; // ------------- MEMORY LEAK HERE -------------
        for(unsigned int i=0; i<len; i++){
            temp[i]=array[i];
        }
        delete [] array;
        array=temp;
    }
}
};

我在这里只发布了与内存相关的代码。

我不明白为什么Valgrind在指定行报告内存泄漏。在将旧数组内容复制到扩大的数组后,我删除了旧数组,两行之后。

同样,如果我在析构函数中取消delete[]函数的注释,我得到一个异常double free or corruption和Valgrind报告一个无效删除(暗示重新删除),所以我完全困惑。什么好主意吗?

编辑:

感谢您的回复!在阅读了注释之后,我发现问题不在于我的类,而在于我用Array类作为参数调用的另一个函数。如果我删除对该函数的调用,并在类中添加delete调用,则不会发生内存泄漏。下面是我的函数:

template <typename T>
void printContents(Array<T> ar){
for(unsigned int i=0; i<ar.size(); i++){
    cout<<"Content at i in array = " << ar.at(i) << endl;
}
}

在阅读了三法则(感谢chris)和Grizzly发布的答案后,我现在明白了为什么删除[]是无效的。因为我没有重载复制构造函数,所以发生了浅复制,因此,我的数组指针被赋值给了ar中的指针,当ar超出作用域时,调用delete[],从而使我在主函数中的删除无效。因此,我得到了异常。如果我删除了delete,那么数组显然会保持分配状态,并导致内存泄漏。

谢谢你的帮助,我投票给灰熊的答案是正确的。

在析构函数中不调用delete[]会泄漏内存。这意味着当您最后一次在对象(对象中的"最终"数组)上调用checkMax时分配的数组永远不会被调用。因此,要解决这个问题,您应该取消析构函数

中的delete[]注释。

正如你提到的,这会给你带来双重免费的问题。这是基于对"三"规则的违反(在c++ 03中是"三"规则,在c++ 11中情况不那么明确,但对于这种情况,"三"规则足以解决您的问题)。三原则基本说明,当定义自定义析构函数、复制构造函数或赋值操作符时,需要定义所有这些函数。在这种情况下,您可能会在某个地方复制Array。这意味着两个实例将包含相同的指针,并尝试在析构函数中删除它。在同一个指针上两次调用delete是错误的。要解决这个问题,您需要定义一个自定义复制构造函数和赋值op,在其中对数组进行深度复制(分配新内存并复制内容)。

抛开这些不谈,我个人建议从智能指针开始,等到你对c++有了更好的掌握,再开始尝试手动内存管理。