如何避免 C++ 中深层嵌套数据结构的析构函数堆栈溢出?
int count;
class MyClass {
std::shared_ptr<void> p;
public:
MyClass(std::shared_ptr<void> f):p(f){
++count;
}
~MyClass(){
--count;
}
};
void test(int n){
std::shared_ptr<void> p;
for(int i=0;i<n;++i){
p = std::make_shared<MyClass>(p);
}
std::cout<<count<<std::endl;
}
int main(int argc, char* argv[])
{
test(200000);
std::cout<<count<<std::endl;
return 0;
}
上述程序会在 Visual Studio 2010 IDE 中的“release”版本下导致堆栈溢出。
问题是:如果您确实需要创建一些像上面这样的数据结构,如何避免这个问题。
更新:现在我看到了一个有意义的答案。然而这还不够好。请考虑我已经更新了 MyClass
以包含两个(或更多)shared_ptr
,并且每个都可以是 MyClass
或其他一些实例数据。
更新:有人为我更新了标题并说“深度引用计数数据结构”,这与这个问题没有必要相关。实际上,shared_ptr
只是一个方便的例子;您可以轻松地更改为具有相同问题的其他数据类型。我还删除了 C++11
标签,因为它也不是 C++11 唯一的问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
像这样,改变 p 的类型,以便我们可以检查它。
最后一些提示:
std::vector>如果您不能将自己限制在 MyClass 中,则可能在 ISharedContainer 接口或其他内容中。
Something like this, with the type of p changed so we can inspect it.
Some final tips:
std::vector<shared_ptr<MyClass>> yieldSharedPtrs()
, perhaps within a ISharedContainer interface or something if you can't restrict yourself to MyClass.感谢@Macke的提示,我有一个改进的解决方案,如下所示:
DEFINE_THREAD_LOCAL是一个宏,它将变量(参数2)定义为具有线程本地存储类型的指定类型(参数1),这意味着有每个正在运行的线程不超过一个实例。由于
thread_local
关键字对于主流编译器仍然不可用,因此我必须假设这样一个宏才能使其适用于编译器。对于单线程程序,
DEFINE_THREAD_LOCAL(type, var)
很简单。此解决方案的好处是它不需要更改类定义。
与@Macke的解决方案不同,我使用
std::queue
而不是std::stack
来保持销毁顺序。在给定的测试用例中,
q.size()
永远不会超过 1。然而,这只是因为该算法是广度优先的。如果MyClass
有更多到MyClass
另一个实例的链接,q.size()
将达到更大的值。注意:重要的是要记住使用 std::move 将 p 传递到队列。如果你忘记这样做,你就没有解决问题,你只是创建和销毁 p 的新副本,并且在可见代码之后,销毁仍然是递归的。
更新:原始发布的代码有一个问题:
q
将在pop()
调用中进行修改。解决方案是缓存q.front()
的值以供以后销毁。Thanks to @Macke's tips, I have an improved solution like the following:
DEFINE_THREAD_LOCAL
is a macro that defines a variable (param 2) as specified type (param 1) with thread local storage type, which means there is no more than one instance for each running thread. Becausethread_local
keyword is still not available for mainstream compilers, I have to assume such a macro to make it work for compilers.For single thread programs,
DEFINE_THREAD_LOCAL(type, var)
is simplyThe benefit of this solution is it do not require to change the class definition.
Unlike @Macke's solution, I use
std::queue
rather thanstd::stack
in order to keep the destruction order.In the given test case,
q.size()
will never be more than 1. However, it is just because this algorithm is breadth-first. IfMyClass
has more links to another instance ofMyClass
,q.size()
will reach greater values.NOTE: It is important to remember use std::move to pass p to the queue. You have not solved the problem if you forgotten to do so, you are just creating and destroying a new copy of p, and after the visible code the destruction will still be recursive.
UPDATE: the original posted code has a problem:
q
is going to be modified withinpop()
call. The solution is cache the value ofq.front()
for later destruction.如果您确实必须使用如此奇怪的代码,则可以增加堆栈的大小。您应该在 Visual Studio 的项目属性中找到此选项。
正如已经建议的,我必须告诉您,在处理大量数据结构时应该避免这种代码,并且如果您计划发布软件,增加堆栈大小并不是一个好的解决方案。显然,如果您滥用此功能,它也可能会严重降低您自己的计算机的速度。
If you really have to work with such an odd code, you can increase the size of your stack. You should find this option in the project properties of Visual Studio.
As already suggested, I must tell you that this kind of code should be avoided when working with a large mole of data structures, and increasing the stack size is not a good solution if you plan to release your software. It may also terribly slow down your own computer if you abuse this feature, obviously.