【问题标题】:Memory Allocation in Recursive C++ Calls递归 C++ 调用中的内存分配
【发布时间】:2010-09-30 03:49:52
【问题描述】:

我在递归 C++ 程序中分配和取消分配内存时遇到问题。因此,如果不使用自动内存管理解决方案,我想知道是否有人可以帮助我解决我遇到的内存泄漏问题。

下面的代码基本上解释了这个问题(虽然这是一个人为的例子,请纠正我所做的任何错误或简化)。

保存数字值的数字类:

class Number {
    public:
        Number() { value = 1; };
        Number& operator + (const Number& n1) const {
            Number result = value + n1.value;
            return result;
        };
        int value;
};

执行递归的两个函数:

Number& recurse(const Number& v1) {
    Number* result = new Number();
    Number one = Number();
    *result = *result + recurse(one);
    return *result;
}

int main(...) {
    Number answer = Number();
    answer = recurse(result);
}

如您所见,递归函数中分配的内存已泄漏,但我不确定根据递归的性质我可以从哪里释放这些内存?

【问题讨论】:

  • “请纠正我所做的任何错误或简化”——您需要将问题设为可编辑以便我们这样做。

标签: c++ memory memory-leaks recursion


【解决方案1】:

您动态分配内存有什么原因吗?

Number recurse(const Number& v1) {
    Number result;
    Number one;
    retun result + recurse(one);
}

我还注意到您没有使用值 v1

但最大的错误是递归有 NO 转义子句。
这实际上是一个无限递归,基本上会耗尽内存。

【讨论】:

    【解决方案2】:

    因此,除了返回 Adam Rosenfield 指出的局部变量的地址之外,我在代码中看到了其他三个问题。

    首先,您的递归函数永远不会结束。在 recurse() 中的某个时刻,您必须检查导致它不再调用 recurse() 并返回的值。这是递归的基本部分。传递的参数 v1 也没有被使用。

    其次,operator+() 实际上不起作用。没有办法将 int 分配给 Number() 对象。

    第三,主要是传递一个从未声明过的名为 result 的东西。

    忘记这些错误,我假设您想要分配堆上的所有对象以避免堆栈溢出,其中此函数将递归多次或实际使用的对象比 Number 大得多。在这种情况下,通过在 recurse() 内的堆上分配返回变量,您将强制调用者删除返回的对象。因此,在 recurse() 和 main() 中调用 recurse() 之后,您必须删除返回的值。向调用者指示的约定是返回指针而不是引用。所以 recurse() 和 main() 看起来像这样:

    Number* recurse(const Number& v1) {
        Number* result = new Number();
        Number one;
        if(v1.value >= 2) {
            Number temp;
            temp.value = v1.value - 1;
            Number* partialResult = recurse( temp ); //capture the object to delete
            *result = *partialResult + one;
            delete partialResult;                    //delete the object
        }
        return result;
    }
    
    int main() {    
        Number result;
        result.value = 15;
        Number *answer;
        answer = recurse(result);
        delete answer;
    }
    

    注意:无论递归实际计算什么都是荒谬的。我不知道意图是什么,但它只是有效的。

    【讨论】:

    • 不是异常安全的。如果您想这样做,请使用 auto_ptr for partialResult
    【解决方案3】:

    我不明白你为什么要在堆上分配内存:

    Number& recurse(const Number& v1) {
        Number result;
        Number one;
    
        // I assume there is a step here to determine if the recursion should stop
    
        result += recurse(one);
        return result;
    }
    

    通过仅在堆栈上分配,您可以保证在函数返回时清除变量。

    否则我认为你必须使用某种智能指针。

    【讨论】:

      【解决方案4】:

      问题出在这里:

      Number& operator + (const Number& n1) const {
          Number result = value + n1.value;
          return result;
      };
      

      您通过引用返回了一个局部变量 (result),这是一个很大的 NO-NO。局部变量在栈上分配,当函数退出时,变量就消失了。返回对局部变量的引用就是返回一个指向堆栈的指针,该堆栈现在正用于其他用途,这会导致很多问题。

      您应该做的是按值返回(只需将返回类型从Number& 更改为Number)。确保您有适当的复制构造函数,或者编译器自动生成的复制构造函数适合您的需要。这意味着当operator+ 返回时,它会创建一个副本(通常可以通过优化消除),并且由于不涉及指针或引用,因此您无法获得损坏的返回值。

      要修复内存泄漏,您可以使用智能指针,例如boost::shared_ptr。或者,完全抛弃指针和动态内存,只返回来自recurse() 的值。

      【讨论】:

        【解决方案5】:

        智能指针是您的朋友。至少快速阅读一下 auto_ptr。

        另外,请阅读 Adam Rosenfield 对您的其他问题的评论(返回对不再存在的值的引用)。

        【讨论】:

          猜你喜欢
          • 2016-01-04
          • 1970-01-01
          • 2011-11-25
          • 1970-01-01
          • 1970-01-01
          • 2014-01-02
          • 1970-01-01
          • 2016-07-28
          • 1970-01-01
          相关资源
          最近更新 更多