【问题标题】:Would deleting a Linked List recursively cause a stack overflow?递归删除链表会导致堆栈溢出吗?
【发布时间】:2016-11-15 17:23:07
【问题描述】:

我有一个由Node 对象构建的链表:

class Node {
   public:
      Node(string data);
      Node* GetNext() const;
      void SetNext(Node* nextNode);
      ~Node();
   private:
      string data;
      Node* next;
};

析构函数定义如下:

Node::~Node() {
   delete next;
}

应该在哪里调用析构函数Node。

我的问题是:像这样删除单链表会导致大列表上的堆栈溢出(它适用于小型列表,即

while (head->GetNext() != 0) {
   Node* temp = head->GetNext();
   head->SetNext(temp->GetNext());
   delete temp;
}
delete head;

Node 没有定义的构造函数?

【问题讨论】:

  • 我没有得到这些奇怪的链表实现,其中节点正在破坏自身和链中的任何其他节点。如果我想从列表中删除单个节点,我会怎么做?更好的选择是创建一个链表类,其中成员是一个节点指针,链表类删除和分配它认为合适的节点。 Node 类根本不应该有任何用户定义的析构函数。
  • 它很容易导致堆栈溢出。在你的链表上抛出一百万个节点并试一试。
  • “会像这样删除单链表导致大列表上的堆栈溢出”。当然,你自己测试应该很容易
  • 迭代版本更适合 Stack Overflow,因为它不是递归的。为什么不在具有Node* head 的class List 中实施销毁?
  • 好吧,我们在这里讨论的是动态分配的指针,所以从技术上讲,我认为我们讨论的是堆溢出。

标签: c++ pointers recursion memory-management linked-list


【解决方案1】:

您的解决方案的问题在于,每次调用 ~Node() 方法时,堆栈帧都会不断地在堆栈上建立。以 3 个 Node 对象为例,从 0x01 开始。删除这些有以下堆栈帧回溯(我假设每个 Node 对象都包含 1 位信息,这是不正确的,但它使列表更整洁):

 (0) Node (0x01): ~Node
 (1) Node (0x02): ~Node
 (2) Node (0x03): ~Node

因此,对于一百万个 Node 对象,您将有一百万个堆栈帧在第一个堆栈帧完成之前。每个堆栈帧都占用堆栈空间,所以,是的,您可能会用完堆栈空间。

迭代解决方案不会遇到这个问题,因为对于每次迭代,删除Node 的调用会在下一个Node 删除例程运行之前完成之前。这意味着您在每次迭代期间在堆栈上都有一个函数调用(加上或减去完成该函数调用所需的数量,但特别是,它不是递归)。

除了这个问题之外,还有另一个问题被提出,那就是您无法删除单个 Node 对象。您可以删除整个列表或列表的一部分。想象一下,如果客户端在前面的示例中引用了Node (0x02),并调用了delete node,会发生什么情况。在这种情况下,Node (0x02) 和Node (0x03) 将被删除,但Node (0x01) 仍会引用指向Node (0x02) 的内存指针。取消引用会导致崩溃。

【讨论】:

    【解决方案2】:

    堆栈大小总是限制函数递归调用的深度。即使使用析构函数(和其他没有参数的函数),也有必要保留该函数调用的返回地址(析构函数无论如何都会将 this 指针作为隐式参数)。

    所以从Nodes 的某个上限开始,堆栈就会溢出,是的。

    迭代方式在这方面更加稳健。

    【讨论】:

      猜你喜欢
      • 2018-10-29
      • 2015-12-08
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 2017-08-23
      相关资源
      最近更新 更多