我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. */
}
请注意,这是一个过于简单的例子,所以显然你不会真正使用上面的代码......所以不需要指出这一点谢谢。
要添加到此,如果我将指针指定给引用怎么办?在这种情况下,我假设数据没有复制......
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;
}
我想回答我自己的问题(在更新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代码的修改后的副本)。
答案 0 :(得分:24)
实际上,d1
和d2
都将被释放,因为它们都在堆栈中。未解除分配的是您在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
逐行,这是你所看到的:
Data
中分配新的getData()
时,会调用默认构造函数。d1
创建Data
。d2
。d2
超出范围时,main()
会调用析构函数。d1
超出范围时,main()
会调用析构函数。请注意,有三个构造函数调用,但只有两个析构函数调用 - 表示您只泄漏了一个Data
对象。
希望有所帮助,
Eric Melski答案 1 :(得分:8)
d1
是一个堆栈对象,由getData()
返回的引用复制构造。 <{1}}已被释放,但d1
中创建的对象已泄露。
答案 2 :(得分:3)
d1
和d2
都是堆栈对象,因此会在其作用域的末尾被销毁,问题是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>