2009-01-24 36 views
6

我有一个C++内存管理疑问,这是(显然)与引用和指针相关。假设我有一类Class与方法my_method这会导致C++中的内存泄漏吗?

OtherClass& Class::my_method(...) { 
    OtherClass* other_object = new OtherClass(...); 
    return *other_object; 
} 

同时在附近的一段代码:

{ 
    Class m(...); 
    OtherClass n; 
    n = m.my_method(...); 
} 

所以,我知道,有关于指针一般规则(〜“什么新 - ed,必须是delete -d“)以避免内存泄漏。但基本上我正在引用我的堆分配对象,所以当n超出范围时,不应该调用OtherClass的析构函数,从而释放先前由other_object指向的内存? 所以最后真正的问题是:这会导致内存泄漏吗?

谢谢。

回答

6

这很明显,你想返回一个新的对象给调用者,你不需要保留任何引用。为此,最简单的方法是按值返回对象。

OtherClass Class::my_method(...) { 
    return OtherClass(...); 
} 

然后在调用代码中,你可以像这样构造新的对象。

{ 
    Class m(...); 
    OtherClass n(m.mymethod(...)); 
} 

这样可以避免返回引用临时对象或要求客户端删除返回的指针的任何顾虑。请注意,这确实需要您的对象是可复制的,但这是一个合法且通常实现的优化,可以在按值返回时避免该副本。

如果您需要共享所有权或对象的生命周期超出调用函数的范围,那么您只需要考虑共享指针或类似指针。在后一种情况下,您可以将此决策留给客户,并仍然按照价值回报。

E.g.

{ 
    Class m(...); 

    // Trust me I know what I'm doing, I'll delete this object later... 
    OtherClass* n = new OtherClass(m.mymethod(...)); 
} 
5

在C++中调用析构函数并释放内存是两件截然不同的事情。

delete确实都调用析构函数并释放内存。 delete[]调用析构函数为分配的元素数量,然后释放内存。

当OtherClass超出范围时,将调用析构函数,但内存不会被释放。作为一个建议,当你觉得你已经彻底理解了C++中的指针时,可以看看智能指针,例如,增强智能指针以减轻C++中的内存管理生命。 (例如,请参见article以获得介绍)

+0

感谢您指点。 – tunnuz 2009-01-24 11:45:39

12

是的,这会导致内存泄漏。

你要做的是,在return语句中,取消引用你创建的新对象。编译器将调用赋值运算符作为返回的一部分,并将新对象的CONTENTS复制到调用方法中分配给它的对象。

新对象将留在堆上,其指针从堆栈中清除,从而造成内存泄漏。

为什么不返回一个指针并以这种方式管理它?

+0

“编译器将调用赋值运算符作为返回的一部分”是不正确的。代码正在返回一个引用。没有需要(或完成)的副本来创建参考。 – Suma 2009-01-24 18:04:37

+0

我的不好;返回将返回对新对象的引用(也可能是一个指针),方法调用中的=会调用赋值运算符。结果仍然是相同的:) – 2009-01-25 07:04:05

2

你有2个OtherClass对象:

一个是N,这是当超出范围在栈上创建,删除成功。

另一个是你在堆上创建的my_method。这个永远不会被删除,并且会导致内存泄漏。

2

通常,当本地对象超出范围时,它的内存是已释放只是因为它被分配到堆栈并自动清除堆栈。由于你的对象被分配在堆上,所以它不可能被自动释放。解除它的唯一方法是明确地调用delete。

我想你也许可以做到这一点:

声明另一个类DummyClass,其中包含一个公共成员是一个指向OtherClass对象。在DummyClass的构造函数中,清除指向NULL的指针。在您的函数中,声明DummyClass类型的对象及其成员指针,以创建另一个类型为OtherClass的对象。然后在DummyClass的析构函数中,检查指针是否为NULL,如果不是,则删除它。通过这种方式,当DummyClass对象超出范围时,将自动清理对象。

2

如果可能,您可以考虑使用std :: auto_ptr或boost/c0x shared_ptr来简化内存管理。

2

如果你坚持堆栈分配,不使用new运营商my_method()并传递一个参考,而不是n,即:

void Class::my_method(OtherClass& other_object, ...) { 
    other_object.init(...); 
} 

然后调用my_method()这样的:

{ 
    Class m(...); 
    OtherClass n; 
    m.my_method(n, ...); 
} 

对于这种模式的工作,Class必须实施某种init()方法od,它允许在不调用构造函数的情况下正确初始化对象。