堆栈溢出与unique_ptr链表

时间:2016-02-21 11:08:49

标签: c++ c++11 linked-list stack-overflow unique-ptr

我已经转换了以下链接列表结构

struct node {
  node* next;
  int v;
};

进入c ++ 11版本 - 没有使用指针。

struct node {
  unique_ptr<node> next;
  int v;
};

添加,删除元素和遍历工作正常,但是当我插入大约1mil的元素时,在调用头节点的析构函数时会出现堆栈溢出。

我不确定我做错了什么。

{
  node n;

  ... add 10mill elements

} <-- crash here

4 个答案:

答案 0 :(得分:7)

正如其他答案中所解释的那样,由于递归的隐式析构函数,你会出现段错误。可以在不使用原始指针的情况下解决这个问题,不得不信任编译器或编写自定义分配器:

~node() {
    for (std::unique_ptr<node> current = std::move(next);
         current;
         current = std::move(current->next));
}

在这里,您可以迭代地浏览指针链。这将一个一个地取消一个指针,并将所有权std::move(current->next)更改为当前。与此同时,由current拥有的前一个未链接指针将在被移动分配覆盖时被释放。

您可能会发现显式变体更直接:

current.reset(current->next.release()));

实际上与:

相同
current = std::move(current->next));

我更喜欢move版本,因为它在任何时候都不会留下原始指针。但在这种情况下,它没有任何区别。

答案 1 :(得分:5)

你这里没有错。

当你创建1000万个元素的列表时,用make_unique分配每个节点一切都很好(当然数据不在堆栈上,除了第一个节点!)。

问题是当你摆脱列表的头部时:unique_ptr将负责删除它拥有的下一个节点,其中还包含一个unique_ptr,它将负责删除下一个节点......等等......

因此,最终会以递归方式删除10百万个元素,每个递归调用都会在堆栈上占用一些空间。

答案 2 :(得分:5)

默认情况下std::unique_ptr会调用仅执行运算符std::default_delete的结构delete的运算符函数。

因此结构std::default_delete的每个运算符函数以递归方式为结构next的数据成员node调用。

结果你得到了堆栈溢出。

如果您使用普通指针而不是类型std::unique_ptr的指针但是以下列方式向结构节点添加了析构函数,您将得到相同的结果

struct node {
  node* next;
  int v;
  ~node() { delete next; } 
};

甚至喜欢

struct node {
  node* next;
  int v;
  ~node() { if ( next ) delete next; } 
};

表示具有大量节点的列表,因为析构函数将以递归方式调用

答案 3 :(得分:3)

因为当你破坏head节点元素时,它会调用析构函数unique_ptr,它会破坏调用第3个元素的析构函数的第二个元素,该元素调用...etс1mil次。

所以,你有1 mil嵌套的函数调用(析构函数)。每个函数调用至少占用堆栈中的内存来存储返回地址(以及参数和局部变量,如果需要的话)。当然,堆栈不能提供这样的内存量。您应该重新编写代码来解决它。例如,重写Node类的析构函数,以便它找到最后一个列表元素,然后在cicle中从末尾销毁它和所有其他节点,而不是再推迟。