这被认为是内存泄漏吗?

时间:2012-04-03 16:51:13

标签: c++ memory-management

假设你有一个这样的简单类:

class foo{
private:
    int* mData;
    int  mSize;
public:
    foo(int size){
        mSize = size;
        mData = new int [mSize];
    }
    ~foo() {
        mSize = 0;
        delete [] mData;
    }
};

然后在主要内部你做:

int main () {
    static int HUGE = 100000000;
    foo a(HUGE);
    // do something useful with a
    // .
    // .
    // .
    // Now I'm done with a; I do not need it anymore ...
    foo b(HUGE);
    // do something useful with b
    // Ok we are done with b
    return 0;
}

正如您所见a之后不再需要b,但由于它是在堆栈上创建的,因此在程序结束之前不会调用析构函数。现在,我知道这与分配new并忘记调用delete不同,但这仍然在浪费内存。你认为这是“内存泄漏”还是只是一个糟糕的编程?

另外,你会如何避免这样的情况?一种方法是在不再需要对象时手动调用析构函数,但是,除了看起来丑陋和不熟悉之外,你会遇到double free的麻烦,除非你将析构函数更改为:

foo::~foo(){
    if (mData != NULL){
        delete [] mData;
        mData = NULL;
        mSize = 0;
    }
}

另一种方法是通过a在堆上创建foo *pa = new foo (HUGE),然后在不再需要该对象时调用delete pa。这有效,但存在引入另一个可能的内存泄漏的危险(如果忘记调用delete pa)。

有没有更好的方法来摆脱不需要的物品?

10 个答案:

答案 0 :(得分:10)

当对象超出范围时,将调用析构函数。 C ++允许函数体内的任意范围。用这种方式写你的主要功能:

int main () {
    static int HUGE = 100000000;

    {
        foo a(HUGE);
        // do something useful with a
        // Now I'm done with a; I do not need it anymore ...
    }

    {
        foo b(HUGE);
        // do something useful with b
        // Ok we are done with b
    }
    // etc.
    return 0;
}

我看到你的例子是简化的,但在真实的程序中,不要忘记

  • operator=
  • 实施适当的复制构造函数和foo
  • 为私有拷贝构造函数和operator=添加声明,因此无法调用它。

答案 1 :(得分:3)

如果您担心范围,只需将巨大的a和b物体放入自己的牙套中即可。

这在技术上并不是内存泄漏,但正如你所说的那样,内存管理非常糟糕。


{
  {
    foo a(HUGE);
  }
  ...

  {
    foo b(HUGE);
  }

答案 2 :(得分:1)

不,它定义为内存泄漏。

内存泄漏是指您分配内存并丢失其句柄,因此您无法在之后释放内存。只要你这样做,在何时何地释放记忆并不重要。

您可以添加封闭范围以强制释放内存:

{
    foo a(HUGE);
}
{
    foo b(HUGE);
}

答案 3 :(得分:1)

这不是内存泄漏,因为不会忘记已分配的内存。然而,这稍微无效,特别是当程序运行时间更长时,应该避免使用。

您可以使用范围缩短对象的生命周期:

int main () {
    static int HUGE = 100000000;
    {
        foo a(HUGE);
        // do something useful with a
        // .
        // .
        // .
        // Now I'm done with a; I do not need it anymore ...
    }
    {
        foo b(HUGE);
        // do something useful with b
        // Ok we are done with b
    }
    return 0;
}

此外,值得重新考虑的是,如果这两部分代码应该在单独的函数中,那么从函数返回时将释放分配的对象。

答案 4 :(得分:1)

类的构造函数也可以将您在'main()'函数中分配的内存块作为参数。这样,一旦使用内存块完成'a',你也可以将它传递给'b'。 'foo'析构函数根本不需要释放任何内存,你根本不需要担心浪费内存或对生命周期。

答案 5 :(得分:0)

  

你认为这是“内存泄漏”还是只是一个糟糕的编程?

不,这不是内存泄漏。

  

你会如何避免这种情况?

写小功能,几行。您的代码将更易读,并且将释放在堆栈上分配的未使用的变量。

答案 6 :(得分:0)

这不是内存泄漏;然而,这恰恰是Firefox开发人员花了很长时间修复的内存使用情况。

范围可能是最简单的解决方法,正如Dark Falcon所暗示的那样。或者,将分配和相关代码移动到单独的函数中。

使用std::auto_ptr可以更安全地处理指针,以便在释放范围时释放它们。

答案 7 :(得分:0)

  

你认为这是“内存泄漏”

不,除非你在中间做像longjmp这样的事情。

  

或只是一个糟糕的节目?

我考虑使用new []在你的类糟糕的编程实践中分配数组,因为你有std :: vector。

  

另外,你会如何避免这种情况?

将foo包含在范围内:

{
    foo a(HUGE);
}
  

除非您将析构函数更改为:

删除忽略空指针。析构函数只调用一次,因此不需要零变量。手动调用析构函数是VERY BAD IDEA - 它不适用于此。如果要重新初始化结构,请实现clear()或resize()方法。

  

有没有更好的方法来摆脱不需要的物品?

是的,将它们放在范围内。

答案 8 :(得分:0)

这不是内存泄漏,但如果你有一个你需要的变量 功能的前半部分,而不是第二部分,很有可能 函数做得很多,应该重构成两个(或者 更多)单独的功能。

答案 9 :(得分:0)

提取功能以缩小范围。给他们好名字:

void do_a(int amount)
{
    foo a(amount);
    // ask `a` to be useful
}

void do_b(int amount)
{
    foo b(amount);
    // ask `b` to be useful
}

int main () {
    static int HUGE = 100000000;

    do_a(HUGE);
    do_b(HUGE);

    return 0;
}