我有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
的定义构造函数?
答案 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)
的内存的指针。取消引用这会导致崩溃。