这会导致C ++中的内存泄漏吗?

时间:2009-01-24 11:29:40

标签: c++ pointers memory-leaks reference memory-management

我对C ++内存管理存在疑问,这显然与引用和指针有关。假设我有一个类Class,其方法为my_method

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

同时在附近的一段代码中:

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

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

感谢。

7 个答案:

答案 0 :(得分:12)

是的,会导致内存泄漏。

您将在return语句中取消引用您创建的新对象。编译器将在返回时调用赋值运算符,并将新对象的CONTENTS复制到调用方法中指定的对象。

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

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

答案 1 :(得分: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( ... ) );
}

答案 2 :(得分:5)

在C ++中调用析构函数并释放内存是两回事。

delete都会调用析构函数并释放内存。 delete[]调用析构函数来获取分配的元素数,然后释放内存。

当OtherClass超出范围时,将调用析构函数,但不释放内存。

<小时/> 作为一个建议,当你觉得你已经完全理解了C ++中的指针时,请研究智能指针,例如:提升智能指针,以减轻C ++中的内存管理生活。 (例如,请参阅article此处的介绍)

答案 3 :(得分:2)

你有2个OtherClass对象:

一个是n,它在堆栈上创建,并在超出范围时成功删除。

另一个是你在my_method中创建的那个。这个永远不会删除,它会导致内存泄漏。

答案 4 :(得分:2)

通常,当本地对象超出范围时,它的内存释放只是因为它在堆栈上分配并且堆栈被自动清理。由于您的对象是在堆上分配的,因此无法自动释放它。释放它的唯一方法是显式调用delete。

我想你可能会这样做:

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

答案 5 :(得分:2)

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

答案 6 :(得分: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()方法,该方法允许在不调用构造函数的情况下正确初始化对象。