【问题标题】:Memory Leak; when deleted exception raised内存泄漏;引发删除异常时
【发布时间】:2013-05-20 22:24:54
【问题描述】:

我正在编写一个可调整大小的数组类 (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 报告一个无效的删除(暗示重新删除),所以我完全糊涂了。 有什么想法吗?

编辑: 感谢您的早期回复!阅读 cmets 后,我发现问题不在于我的类,而在于我正在调用的另一个函数 以 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;
}
}

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

感谢您的帮助,我已将灰熊的答案投票为正确。

【问题讨论】:

  • 细说三评论的规则,你是不是从一个Array复制到另一个Array,会导致双删?
  • @chris 是的,我也是这么想的。 Sumanth,您是在任何地方按值传递 Array 作为参数,还是以其他方式创建副本?您是否有正确处理 array 指针的复制构造函数?如果您缺少复制构造函数,则默认行为是仅复制指针,这意味着您将有两个指向同一个 array 的指针,因此是双重删除。

标签: c++ memory-leaks valgrind


【解决方案1】:

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

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

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

【讨论】:

  • 很好的答案,但我不同意你帖子的最后一点,我认为在你学习的时候应该理解手动内存管理,因为目标是理解,而不是让事情发挥作用。 (我们可能都通过做海报在某个时候所做的事情来了解这一点!)
  • @wilsonmichaelpatrick:我并不是说人们永远不应该学习手动内存管理,只是不应该从那开始(就像 OP 似乎正在做的那样)。即使不考虑手动内存管理,C++ 也足够难,智能指针并不能神奇地解决所有问题,而只是使它们更加明显(unique_pointer 会简单地禁止复制,直接导致为什么和什么的问题做)
猜你喜欢
  • 1970-01-01
  • 2012-07-24
  • 2018-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多