[英]Segfault using std::shared_ptr during destruction likely due to too many function calls on the stack
以下代码编译并运行正常:
#include <memory>
struct MyTree {
std::shared_ptr <MyTree> left;
std::shared_ptr <MyTree> right;
int val;
MyTree(
std::shared_ptr <MyTree> left_,
std::shared_ptr <MyTree> right_,
int val_
) : left(left_), right(right_), val(val_) {};
};
int main() {
std::shared_ptr <MyTree> t(
new MyTree( std::shared_ptr <MyTree>(),
std::shared_ptr <MyTree>(),
0)
);
for(int i=0;i<10000;i++) {
t.reset(new MyTree(t,t,0));
}
}
但是,当for循环从10000更改为100000时,我收到段错误。 查看gdb中的结果,看起来像std :: shared_ptr中的垃圾收集调用的析构函数会创建一个数千深的回溯。 因此,我认为段错是由于函数调用从堆栈中耗尽。 我有两个问题。 首先,这是对segfault的正确评估吗? 其次,如果是这样,是否有一种很好的方法来管理自定义数据结构,例如需要进行垃圾回收的树,但可能非常大。 谢谢。
这通常不是问题,因为通常你保持树平衡,深度为O(lg N)。
相反,你得到了一个奇怪的单链表,每个指针都有一个副本。 那是......奇怪的。
一个真正的单链表将是非常深的递归,但可能会受益于尾调用优化而不会耗尽堆栈。
您遇到的问题对于混合两种数据结构而言非常独特。 哪个没有我能看到的好处。
您的评估对我来说完全正确。 看起来删除子子树的递归调用超过了yoru堆栈大小。 这与shared_ptr
无关,但我预计数据结构上的任何递归算法也会以相同的方式失败。
如果可能在您的平台上,处理大型结构需求的最简单方法就是增加堆栈的大小(例如ulimit
)以允许自然递归算法运行。
如果那是不可能的,那么你将不得不自己遍历节点,将遍历的结果存储到某种容器中,这样你就可以切割子节点而不需要对树结构进行全深度遍历。
这看起来像是滥用std::shared_ptr
。 还有一些非常糟糕的命名:你的类MyTree
不是一棵树,而只是一个节点。 树应该是一个单独的类,并应删除其析构函数中的所有节点。
话虽如此,对于手头的问题,这不会有太大变化。 你以递归方式访问树上的节点(关于唯一有意义的方法),如果你让树太深,堆栈就会溢出,无论访问是否是隐式的(通过std::shared_ptr
的析构函数调用std::shared_ptr
)或显式。 创建这样的树是没有意义的,因为在开始破坏它之前创建一个无法访问其节点的树是没有意义的。
在评论中考虑垃圾收集的讨论。 使用Boehm收集器或其他一些垃圾收集器,将解决释放元素的问题。 但它仍然不允许你在释放之前访问它们,所以这样的树仍然无用。 (我认为在C ++中有很强的支持垃圾收集的论据,但这不是其中之一。)
我认为这种类型的错误可以在没有shared_ptr的情况下重现。 这基本上是一个很长的递归调用。 好的,我刚刚创建并删除了一半的树但是......
struct MyTree {
MyTree *left;
int val;
MyTree()
{
left = 0;
}
MyTree(MyTree *left_)
: left(left_) {};
~MyTree()
{
delete left;
}
};
int main() {
MyTree *t = new MyTree();
for(int i=0;i<100000;i++) {
t = new MyTree(t);
}
delete t;
}
只需在100000之后再添加一个零,就会得到相同的错误消息
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.