我是SWE学习C ++,并使用std :: unique_ptr构建一个简单的LinkedList类作为对head
和next
的引用。这是基本结构:
template <class T>
struct LinkedListNode {
T value;
std::unique_ptr<LinkedListNode<T>> next;
// For debugging.
~LinkedListNode() {
std::cout << "destructed for value " << this->value << std::endl;
}
};
template <class T>
struct LinkedList {
std::unique_ptr<LinkedListNode<T>> head;
};
使用智能指针,我希望当LinkedList实例被删除或超出范围时,head
将被删除,每个next
节点也将被递归删除。
这正是发生的事情。但是,当使用很长的列表(大约20M节点)时,它仍然可以正常工作。不应该因堆栈溢出而崩溃吗?
为了粗略估计操作系统堆栈的大小,我编写了以下脚本:
int main() {
struct s {
static void p(int i) {
std::cout << i << std::endl;
p(i+1);
};
s::p(0);
}
它在迭代次数~175K时崩溃,远远低于之前我能够解除分配的20M节点。到底是怎么回事?我错过了关于unique_ptr工作方式的一些内容吗?
答案 0 :(得分:2)
在你的例子中你确实有递归,它背后没有达到堆栈溢出的真正原因可能是因为它是一个tail-call recursion,可以针对迭代解决方案进行优化。
使用以下代码片段:
struct Node
{
int value;
std::unique_ptr<Node> next;
Node(int value, Node* next) : value(value), next(next) { }
~Node()
{
if (value == 0)
cout << "foo" << endl;
}
};
int main()
{
Node* node = new Node(0, nullptr);
for (int i = 1; i <= 5; ++i)
node = new Node(i, node);
delete node;
return 0;
}
通过在cout
语句上放置断点并检查堆栈跟踪,您可以清楚地看到该行为是递归的:
通过使用基础析构函数跟踪~Node()
何时返回,行为也显示为here。
由于必须在从析构函数返回之前销毁next
节点,这会导致再次调用~Node()
。通过使用原始指针并直接在析构函数中删除下一个指针,这种行为是相同的,确实已经回答了here。