2

我正在编写一个可调整大小的数组类(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 类作为参数调用的另一个函数。如果我删除对该函数的调用,并在类中添加删除调用,则不会发生内存泄漏。这是我的功能:

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;
}
}

在阅读了三法则(感谢克里斯)和灰熊发布的答案之后,我现在明白了为什么 delete[] 无效。因为我没有重载复制构造函数,所以发生了浅拷贝,因此,我的数组指针被分配给了 ar 中的指针,当 ar 超出范围时,调用了 delete[],从而使我的 delete 在 main功能无效。因此,我得到了例外。如果我删除了删除,那么数组显然会保持分配状态并导致内存泄漏。

感谢您的帮助,我投票认为灰熊的回答是正确的。

4

1 回答 1

2

您通过不调用delete[]析构函数来泄漏内存。这意味着checkMax永远不会调用在您最后一次调用对象时分配的数组(对象中的“最终”数组)。所以要解决这个问题,你应该取消delete[]在析构函数中的注释

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

抛开所有事情不谈,我个人建议从智能指针开始,然后等你更好地掌握 C++ 时,开始尝试手动内存管理。

于 2013-05-25T13:55:37.893 回答