分配有新内存的内存是否自动释放?

时间:2009-04-19 19:55:59

标签: c++ memory-management

我99%肯定这个问题的答案是盲目的没有。请验证我的命题,即以下代码会产生内存泄漏。

Data &getData()
{
    Data *i = new Data();
    return *i;
}

void exampleFunc()
{
    Data d1 = getData();
    Data d2;

    /* d1 is not deallocated because it is on the heap, and d2 is
     * because it is on the stack. */
}

请注意,这是一个过于简单的例子,所以显然你不会真正使用上面的代码......所以不需要指出这一点谢谢。

更新1:

要添加到此,如果我将指针指定给引用怎么办?在这种情况下,我假设数据没有复制......

Data &getData()
{
    Data *i = new Data();
    return *i;
}

void exampleFunc()
{
    // Does copying occur here?
    Data &d1 = getData();

    // Does this deallocate the memory assigned to the pointer?
    delete &d;
}

更新2:

我想回答我自己的问题(在更新1中)以下代码证明分配对引用的引用不会导致副本...

#include <iostream>
#include <string>

using namespace std;

class Data
{
public:
    string mName;

    Data(const string &name) : mName(name)
    { cout << mName << " default ctor" << endl; }

    Data(const Data& other)
    {
        mName = other.mName + " (copy)";
        cout << mName << " copy ctor" << endl;
    }

    ~Data()
    { cout << mName << " dtor" << endl; }

    static Data &getData(const string &name)
    {
        Data *d = new Data(name);
        return *d;
    }
};

int main()
{
    cout << "d1..." << endl;
    Data d1 = Data::getData("d1");

    cout << "d2..." << endl;
    Data d2("d2");

    cout << "d3..." << endl;
    Data &d3 = Data::getData("d3");

    cout << "return..."  << endl;
    return 0;
}

产生以下结果......

d1...
d1 default ctor
d1 (copy) copy ctor
d2...
d2 default ctor
d3...
d3 default ctor
return...
d2 dtor
d1 (copy) dtor

感谢Eric Melski为a great answer(我在更新2中的代码是他的exmaple代码的修改后的副本)。

7 个答案:

答案 0 :(得分:24)

实际上,d1d2都将被释放,因为它们都在堆栈中。未解除分配的是您在Data函数中分配的getData()对象。如果使用构造函数和析构函数中的检测功能充实Data类,则可以更清楚地看到这一点。例如:

class Data {
public:
    Data() { cout << "Data default ctor" << endl; }
    Data(const Data& other) { cout << "Data copy ctor" << endl; }
    ~Data() { cout << "Data dtor" << endl; }

    static Data& getData()
    {
        Data *i = new Data();
        return *i;
    }
};

请注意,我已明确声明了Data的复制构造函数。在您的示例中,当您执行Data d1 = getData();时,您隐式调用该构造函数,并且我怀疑这是您的困惑所在。现在,如果我运行这个简单的程序:

#include <iostream>
using namespace std;

int main(int argc, char *argv[])
{
    Data d1 = Data::getData();
    Data d2;

    return 0;
}

输出如下:

Data default ctor
Data copy ctor
Data default ctor
Data dtor
Data dtor

逐行,这是你所看到的:

  1. Data中分配新的getData()时,会调用默认构造函数。
  2. 调用复制构造函数以从您刚制作的动态分配的d1创建Data
  3. 调用默认构造函数来创建d2
  4. d2超出范围时,main()会调用析构函数。
  5. d1超出范围时,main()会调用析构函数。
  6. 请注意,有三个构造函数调用,但只有两个析构函数调用 - 表示您只泄漏了一个Data对象。

    希望有所帮助,

    Eric Melski

答案 1 :(得分:8)

d1是一个堆栈对象,由getData()返回的引用复制构造。 <{1}}已被释放,但d1中创建的对象已泄露。

答案 2 :(得分:3)

d1d2都是堆栈对象,因此会在其作用域的末尾被销毁,问题是getData()创建了一个永不删除的新堆对象。 d1从对此堆对象的引用进行了初始化,d1本身将在exampleFunc的末尾被正确销毁,但每次调用{{1}时生成的堆对象}不会被删除。

使用getData()的签名可以删除此对象,但是返回对需要删除的内容的引用不是惯用的界面。

工作,但不是一个好的界面:

getData()

答案 3 :(得分:3)

当你重新获得记忆时,唯一一次当程序终止时OS会自动回收它。在程序执行期间,您发布的内容将导致内存泄漏,因为C ++没有内置垃圾收集器,并且需要对堆分配的对象进行手动内存管理。

答案 4 :(得分:1)

这是Java / C#程序员的典型“思维方式”。

在C ++中,您实际上可能比您想象的更多地使用值类型:

Data getData()
{
    Data i;
    return i;
}

void exampleFunc()
{
    Data d1 = getData();
    /* d1 is constructed here and destroyed */
    Data d2;

}

如果你想返回指针,例如当你有一些派生类只使用允许移动所有权的auto_ptr等智能指针时:

auto_ptr<Data> getData()
{
    auto_ptr<Data> i(new Data());
    return i;
}

void exampleFunc()
{
    auto_ptr<Data> d1 = getData();
    /* now d1 is destroyed when goes out of scope */
    Data d2;
}

答案 5 :(得分:0)

你是对的。您必须使用delete或delete []显式释放内存。

答案 6 :(得分:0)

准确地说,你的问题的答案是肯定的。

在此代码中:

int main()
{
Data* d = new Data();
return 0;   //end of execution
}

执行结束时,操作系统会自动释放 d 指向的数据。

换句话说,由程序完成执行但未被它(程序)取消分配的所有数据将在执行后(而不是在执行期间)由操作系统取消分配。 / p>