C ++ smart_ptr不会导致堆栈溢出?

时间:2015-11-08 15:20:07

标签: c++ pointers stack overflow unique-ptr

我是SWE学习C ++,并使用std :: unique_ptr构建一个简单的LinkedList类作为对headnext的引用。这是基本结构:

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工作方式的一些内容吗?

1 个答案:

答案 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语句上放置断点并检查堆栈跟踪,您可以清楚地看到该行为是递归的:

enter image description here

通过使用基础析构函数跟踪~Node()何时返回,行为也显示为here

由于必须在从析构函数返回之前销毁next节点,这会导致再次调用~Node()。通过使用原始指针并直接在析构函数中删除下一个指针,这种行为是相同的,确实已经回答了here