【问题标题】:Will this lead to a memory leak in C++?这会导致 C++ 中的内存泄漏吗?
【发布时间】:2010-10-03 07:37:17
【问题描述】:

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

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

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

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

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

谢谢。

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    【讨论】:

    • “编译器将调用赋值运算符作为返回的一部分”不正确。代码正在返回一个引用。创建引用不需要(或完成)副本。
    • 我的错; return 将返回对新对象的引用(也可能是指针),方法调用中的 = 将调用赋值运算符。结果还是一样:)
    【解决方案2】:

    很明显,您希望向调用者返回一个不需要保留任何引用的新对象。为此,最简单的做法是按值返回对象。

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

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

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

    这避免了返回对临时对象的引用或要求客户端管理删除返回的指针的任何担忧。请注意,这确实要求您的对象是可复制的,但这是一种合法且通常实现的优化,以便在按值返回时避免复制。

    如果您需要共享所有权或对象的生命周期超出调用函数的范围,您只需要考虑共享指针或类似的东西。在后一种情况下,您可以将决定权留给客户端,但仍按值返回。

    例如

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

    【讨论】:

      【解决方案3】:

      调用析构函数和释放内存在 C++ 中是两个不同的事情。

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

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


      作为一个建议,当你觉得你已经彻底理解了 C++ 中的指针时,看看智能指针,例如提升智能指针以简化 C++ 中的内存管理寿命。 (例如,请参阅此处的article 进行介绍)

      【讨论】:

        【解决方案4】:

        您有 2 个 OtherClass 对象:

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

        另一个是您在堆上创建的,在 my_method 中。这个永远不会被删除,会导致内存泄露。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            我认为您可能可以这样做:

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

            【讨论】:

            • 其实我并不清楚智能指针是如何实现的。反正我想不出更简单但通用的方法……
            【解决方案7】:

            如果您坚持堆栈分配,请不要在my_method() 中使用new 运算符,而是传递对n 的引用,即:

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

            然后像这样调用my_method()

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

            要使这种模式起作用,Class 必须实现某种init() 方法,该方法允许在不调用构造函数的情况下正确初始化对象。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多