是否会以递归方式删除链接列表导致堆栈溢出?

时间:2016-11-15 17:23:08

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

我有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上调用析构函数。

我的问题是:将这样删除单链表导致大型列表上的堆栈溢出(它在小的,即< 10大小列表上工作正常)。如果是,那么使用迭代解决方案会更好,比如

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

哪里没有Node的定义构造函数?

2 个答案:

答案 0 :(得分:4)

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

因此,从Node的某个上限开始,堆栈将溢出,是的。

迭代方式对此更加健壮。

答案 1 :(得分:4)

您的解决方案的问题是,对于每次调用~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)将_still引用指向Node (0x02)的内存的指针。取消引用这会导致崩溃。