展开C++中的异常和堆栈
在 C++ 异常结构,控件从 throw 语句的移动到可处理该引发类型的第一个 catch 语句。 在 catch 语句时,在引发和捕捉语句之间范围内的任何自动变量在称为 堆栈展开的进程被销毁。 在展开的堆栈,执行如下所示:
控件由正常顺序执行到达 try 语句。 在 try 中控制的部分执行块。
如果在控制节执行过程中为引发异常,try 块之后的 catch 子句不会执行。 继续执行在语句中,在后面关联的 try 后的最后一 catch 子句块。
如果在所控制的执行过程中引发或了控制的部分直接或间接调用的实例,异常对象从由 throw 操作数创建的对象创建的。 (这意味着有可能涉及复制构造函数。)此时,编译器可以在句柄类型,将引发异常或 catch 处理程序中可以处理任何类型的异常的更高的执行上下文查找 catch 子句。 在 try 块之后,catch 处理程序会检查遵循其外观的序列。 如果未找到适当的处理程序,则检查下一个动态封闭的 try 块。 直到最外面的封闭 try 块进行检查前,此过程将继续。
如果仍未找到匹配的处理程序,或者如果发生异常,在展开过程,但,在处理程序中获取控件之前,预定义的运行时功能 terminate 调用。 如果出现异常,在引发异常后,但,在展开开始之前,terminate 调用。
如果找到匹配的 catch 处理程序,并且它通过值来捕获,则通过复制异常对象来初始化其形参。 如果通过引用捕获,初始化参数来引用异常对象。 在形参初始化后,展开堆栈的进程开始。 此过程包括完全是构造,但不构在 try 开头之间块与 catch 处理程序和异常的引发站点的任何自动对象的损坏。 析构以构造相反顺序发生。 catch 处理程序中执行和过程继续执行,在最后一个处理程序是后,在不是 catch 处理程序的第一个语句或构造。 控件通过引发的异常仅输入 catch 处理程序,传递 goto 语句或 case 标签。switch 语句。
“堆栈展开示例”
以下示例演示引发异常时,该堆栈如何展开。 在线程的执行与在 C 的 throw 语句中跳转到 maincatch 语句,并展开每个功能。 请注意 Dummy 对象被创建并被销毁的顺序,但超出范围。 和函数未完成除 main,包含 catch 语句的通知。 功能 A 从其从不返回对 B(),并且,B 从其从不返回对 C()。 如果取消注释 Dummy 指针的定义和对应 delete 语句,然后运行程序,请注意指针不会删除。 这将显示可能发生的问题,当函数不提供一个异常确保时。 有关更多信息,请参见如何:设计异常。 如果批注 catch 语句,可以观察发生了什么由于未经处理的异常,那么,当程序终止。
#include <string>
#include <iostream>
using namespace std;
class MyException{};
class Dummy
{
public:
Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
~Dummy(){ PrintMsg("Destroyed Dummy:"); }
void PrintMsg(string s) { cout << s << MyName << endl; }
string MyName;
int level;
};
void C(Dummy d, int i)
{
cout << "Entering FunctionC" << endl;
d.MyName = " C";
throw MyException();
cout << "Exiting FunctionC" << endl;
}
void B(Dummy d, int i)
{
cout << "Entering FunctionB" << endl;
d.MyName = "B";
C(d, i + 1);
cout << "Exiting FunctionB" << endl;
}
void A(Dummy d, int i)
{
cout << "Entering FunctionA" << endl;
d.MyName = " A" ;
// Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
B(d, i + 1);
// delete pd;
cout << "Exiting FunctionA" << endl;
}
int main()
{
cout << "Entering main" << endl;
try
{
Dummy d(" M");
A(d,1);
}
catch (MyException& e)
{
cout << "Caught an exception of type: " << typeid(e).name() << endl;
}
cout << "Exiting main." << endl;
char c;
cin >> c;
}
/* Output:
Entering main
Created Dummy: M
Copy created Dummy: M
Entering FunctionA
Copy created Dummy: A
Entering FunctionB
Copy created Dummy: B
Entering FunctionC
Destroyed Dummy: C
Destroyed Dummy: B
Destroyed Dummy: A
Destroyed Dummy: M
Caught an exception of type: class MyException
Exiting main.
*/