在销毁期间使用std::shared_ptr发生段故障,可能是由于堆栈上的函数调用太多
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中的垃圾收集导致的析构函数调用创建了数千个深度的回溯。因此,我认为段故障是由于从函数调用的堆栈上的空间耗尽。我有两个问题。首先,这是对分段断层的正确评估吗?其次,如果是这样,是否有一种好的方法来管理自定义数据结构,如需要垃圾收集的树,但可能非常大。谢谢。
这通常不是问题,因为通常你会保持树的平衡,并且深度是O(lgn)。
你得到了一个奇怪的单链表,每个指针都有一个副本。这是……奇怪。
一个真正的单链表将是非常深的递归,但可能受益于尾部调用优化,而不会耗尽堆栈。
你所遇到的问题是你混合了这两种数据结构所造成的。我看不出有什么好处
我看你的评估完全正确。看起来删除子树的递归调用超出了您的堆栈大小。这与shared_ptr
无关,尽管我希望数据结构上的任何递归算法也以同样的方式失败。
如果可能的话,在你的平台上,处理这种大型结构的最简单的方法就是简单地增加堆栈的大小(例如ulimit
),以允许自然递归算法发挥作用。
如果这是不可能的,你将不得不自己遍历节点,将遍历的结果存储到某种类型的容器中,这样你就可以切掉子节点,而不需要对树结构进行完整的深度遍历。
这看起来像是滥用std::shared_ptr
。和一些非常糟糕的命名:您的类MyTree
不是树,而是简单的一个节点。树应该是一个单独的类,并且应该删除析构函数中的所有节点。
已经说过,这不会改变太多关于手头有问题。你访问的是树的节点递归的(这是唯一有意义的方法),如果你如果树太深,栈就会溢出,不管怎样访问是否是隐式的(通过析构函数)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之后再加一个零你会得到同样的错误信息
- C++析构函数调用两次,堆栈分配的复合对象
- 堆栈展开如何与析构函数调用有关?
- C++ 使函数调用依赖于模板参数
- 严格别名规则是否适用于跨函数调用
- 'ShowSUM':__declspec(dllexport)不能应用于具有__clrcall调用约定的函数
- 在堆栈已满之前,在C/C 中以最大递归函数调用并给出分段故障
- 函数调用时局部变量在堆栈中的组织方式
- 汇编函数调用是否会导致所有寄存器被推送到堆栈中
- 线程的调用堆栈中充满了相同的函数调用--curl_inet_ntop()
- 此代码是否依赖于函数调用顺序未定义的行为
- 何时构造函数调用中的堆栈对象
- 为什么构造函数调用依赖于默认析构函数的存在?
- C堆栈跟踪中缺少函数调用
- 如何使堆栈在函数调用中工作
- c++ 函数调用的参数推送顺序不反映堆栈中参数的地址
- 在函数调用时获得堆栈溢出
- 理解C/ c++中函数调用的堆栈框架
- 使用虚函数调用时节省堆栈空间
- 函数参数列表中的函数调用是否会加深堆栈?
- 使C函数指针与C++中基于C样式堆栈的调用机制一起工作