什么是栈展开?搜索了一遍,但没有找到有启发性的答案!
当前回答
所有这些都与c++有关:
定义: 当您静态地创建对象(在堆栈上而不是在堆内存中分配对象)并执行函数调用时,它们是“堆叠起来的”。
当一个作用域(由{和}分隔的任何内容)退出(通过使用return XXX;,到达作用域的末尾或抛出异常)时,该作用域内的所有内容都被销毁(对所有内容调用析构函数)。这个销毁局部对象并调用析构函数的过程称为堆栈展开。
您有以下与堆栈展开相关的问题:
avoiding memory leaks (anything dynamically allocated that is not managed by a local object and cleaned up in the destructor will be leaked) - see RAII referred to by Nikolai, and the documentation for boost::scoped_ptr or this example of using boost::mutex::scoped_lock. program consistency: the C++ specifications state that you should never throw an exception before any existing exception has been handled. This means that the stack unwinding process should never throw an exception (either use only code guaranteed not to throw in destructors, or surround everything in destructors with try { and } catch(...) {}).
如果任何析构函数在堆栈展开过程中抛出异常,那么就会出现未定义的行为,这可能导致程序意外终止(最常见的行为)或整个宇宙结束(理论上是可能的,但在实践中尚未观察到)。
其他回答
在一般意义上,堆栈“unwind”几乎等同于函数调用的结束和随后的堆栈弹出。
然而,特别是在c++的情况下,堆栈展开必须与c++如何调用自任何代码块开始分配的对象的析构函数有关。在块中创建的对象将按照其分配的相反顺序被释放。
c++运行时销毁在throw和catch之间创建的所有自动变量。在下面这个简单的例子中,f1()抛出和main()捕获,在B和A类型的对象之间按此顺序在堆栈上创建。当f1()抛出时,将调用B和A的析构函数。
#include <iostream>
using namespace std;
class A
{
public:
~A() { cout << "A's dtor" << endl; }
};
class B
{
public:
~B() { cout << "B's dtor" << endl; }
};
void f1()
{
B b;
throw (100);
}
void f()
{
A a;
f1();
}
int main()
{
try
{
f();
}
catch (int num)
{
cout << "Caught exception: " << num << endl;
}
return 0;
}
这个程序的输出将是
B's dtor
A's dtor
这是因为f1()抛出时程序的调用堆栈看起来像
f1()
f()
main()
因此,当f1()被弹出时,自动变量b被销毁,然后当f()被弹出时,自动变量a被销毁。
希望对大家有所帮助,编码愉快!
我不知道你是否读过这篇文章,但维基百科关于调用堆栈的文章有一个很好的解释。
解除:
Returning from the called function will pop the top frame off of the stack, perhaps leaving a return value. The more general act of popping one or more frames off the stack to resume execution elsewhere in the program is called stack unwinding and must be performed when non-local control structures are used, such as those used for exception handling. In this case, the stack frame of a function contains one or more entries specifying exception handlers. When an exception is thrown, the stack is unwound until a handler is found that is prepared to handle (catch) the type of the thrown exception. Some languages have other control structures that require general unwinding. Pascal allows a global goto statement to transfer control out of a nested function and into a previously invoked outer function. This operation requires the stack to be unwound, removing as many stack frames as necessary to restore the proper context to transfer control to the target statement within the enclosing outer function. Similarly, C has the setjmp and longjmp functions that act as non-local gotos. Common Lisp allows control of what happens when the stack is unwound by using the unwind-protect special operator. When applying a continuation, the stack is (logically) unwound and then rewound with the stack of the continuation. This is not the only way to implement continuations; for example, using multiple, explicit stacks, application of a continuation can simply activate its stack and wind a value to be passed. The Scheme programming language allows arbitrary thunks to be executed in specified points on "unwinding" or "rewinding" of the control stack when a continuation is invoked.
检查[编辑]
我读了一篇博客文章,帮助我理解。
What is stack unwinding? In any language that supports recursive functions (ie. pretty much everything except Fortran 77 and Brainf*ck) the language runtime keeps a stack of what functions are currently executing. Stack unwinding is a way of inspecting, and possibly modifying, that stack. Why would you want to do that? The answer may seem obvious, but there are several related, yet subtly different, situations where unwinding is useful or necessary: As a runtime control-flow mechanism (C++ exceptions, C longjmp(), etc). In a debugger, to show the user the stack. In a profiler, to take a sample of the stack. From the program itself (like from a crash handler to show the stack). These have subtly different requirements. Some of these are performance-critical, some are not. Some require the ability to reconstruct registers from outer frame, some do not. But we'll get into all that in a second.
你可以在这里找到完整的帖子。
在Java堆栈中,不宽或不伤人不是很重要(使用垃圾收集器)。在许多异常处理论文中,我看到了这个概念(堆栈展开),特别是那些作者在C或c++中处理异常处理。对于try catch块,我们不应该忘记:在局部块之后释放所有对象的堆栈。