C++语言处理异常 try
C++语言提供对处理异常情况的内部支持,异常情况即是所知道的“异常”,可能在你的程序执行期间出现。
try、throw和catch语句已被加到C++语言中去实现异常处理。有了C++异常处理,你的程序可以向更高的执行上下文传递意想不到的事件,这些上下文能更好地从这些异常事件中恢复过来。这些异常由正常控制流外的代码进行处理。Microsoft C++编译器朝着C++进化中的标准去实现基于ISO WG21/ANSI X3J16工作文件的C++异常处理模式。
try子句后的复合语句是代码的保护段。throw表达式“抛出”(凸起)一个异常,catch子句后的复合语句是异常处理器,“捕获”(处理)由throw表达式抛出的异常。异常说明语句指示子句处理的异常的类型,类型可以是任何有效的数据类型,包括C++的类。如果异常说明语句是一个省略号(...),catch子句处理任何类型的异常,包括C的异常。这样的处理器必须是其try块的最后一个处理器。
throw的操作数语法上与return语句的操作数相似。注意:Microsoft C++不支持函数throw特征机制,如ANSI C++草案的15.5节所描述的。此外,它也不支持ANSI C++草案的15节中描述的function-try-block。执行过程如下:
1. 控制通过正常的顺序执行到达try语句,保护段(在try块内)被执行。
2. 如果在保护段执行期间没有引起异常,跟在try块后的catch子句不执行。从异常被抛出的try块后跟随的最后一个catch子句后面的语句继续执行下去。
3. 如果在保护段执行期间或在保护段调用的任何例行程序中(直接或间接的调用)有异常被抛出,则从通过throw操作数创建的对象中创建一个异常对象(这隐含指可能包含一个拷贝构造函数)。在此点,编译器在能够处理抛出类型的异常的更高执行上下文中寻找一个catch子句(或一个能处理任何类型异常的catch处理器)。catch处理程序按其在try块后出现的顺序被检查。如果没有找到合适的处理器,则下一个动态封闭的try块被检查。此处理继续下去直到最外层封闭try块被检查完。
4. 如果匹配的处理器未找到,或如果在不自动分行时出现异常,但在处理器得到控制之前预定义的运行函数terminate被调用。如果一个异常发生在抛出异常之后,则在循环展开开始之前调用terminate。
5. 如果一个匹配的catch处理器被找到,且它通过值进行捕获,则其形参通过拷贝异常对象进行初始化。如果它通过引用进行捕获,则参量被初始化为指向异常对象,在形参被初始化之后,“循环展开栈”的过程开始。这包括对那些在与catch处理器相对应的try块开始和异常抛出地点之间创建的(但尚未析构的)所有自动对象的析构。析构以与构造相反的顺序进行。catch处理器被执行,且程序恢复到跟随在最后的处理器之后的执行(即不是catch处理器的第一条语句或构造)。控制仅能通过一个抛出的异常输入一个catch处理器,而永远不能通过goto语句或switch语句中的case标号。
以下是一个try块和其相应的catch处理器的简单例子,此例子检测使用new运算符的存储器分配操作的失败。如果new成功了,则catch处理器永不执行:
#include <iostream.h>
int main()
{
char *buf;
try
{
buf=new char[512];
if(buf==0)
throw "Memory allocation failure!";
}
catch (char *str)
{
cout << "Exception raised: " << str <<′\n′;
}
//...
return 0;
}
throw表达式的操作数指示一个char*类型的异常正被抛出。它由表示有捕获char*类型的一个异常的能力的catch处理器进行处理。在存储器分配失败事件中,这是从前面例子得到的输出:
Exception raised: Memory allocation failure!
C++异常处理的真正能力不仅在于其处理各种不同类型的异常的能力,还在于在堆栈循环展开期间为异常抛出前构造的所有局部对象自动调用析构函数的能力。
以下例子演示了使用带析构语义的类的C++异常处理:
#include <iostream.h>
void MyFunc(void);
class CTest
{
public:
CTest() {};
~CTest() {};
const char *ShowReason() const { return "Exception in CTest class.";}
};
class CDtorDemo
{
public:
CDtorDemo();
~CDtorDemo();
};
CDtorDemo::CDtorDemo()
{
cout << "Constructing CDtorDemo.\n";
}
CDtorDemo::~CDtorDemo()
{
cout << "Destructing CDtorDemo.\n";
}
void MyFunc()
{
CDtorDemo D;
cout << "In MyFunc(). Throwing CTest exception.\n";
throw CTest();
}
int main()
{
cout << "In main.\n";
try
{
cout << "In try block, calling MyFunc().\n";
MyFunc();
}
catch (CTest E)
{
cout << "In catch handler.\n";
cout << "Caught CTest exception type:";
cout << E.ShowReason() << "\n";
}
catch (char *str)
{
cout << "Canght some other exception:" << str << "\n";
}
cout << "Back in main. Execution resumes here.\n";
return 0;
}
以下是上面例子的输出:
In main.In try block, calling MyFunc()
.Constructing CDtorDemo.
In MyFunc(). Throwing CTest exception.
Destructing CDtorDemo.
In catch handler.
Caught CTest exception type; Exception in CTest class.
Back in main. Execution resumes here.
注意在此例中,异常参量(catch子句的参量)在两个catch处理器中都被说明:
catch(CTest E)
//...
catch(char *str)
//...
你不需要说明此参量;在很多情况下可能通知处理器有某个特定类型的异常已经产生就足够了。但是如果你在异常说明中没有说明一个异常对象,你将无法访问catch处理程序子句中的那个对象。
一个不带操作数的throw表达式把当前正被处理的异常再次抛出,这样一个表达式仅仅应该出现在一个catch处理器中或从catch处理器内部被调用的函数中,再次抛出的异常对象是源异常对象(不是拷贝)。例如:
try
{
throw CSomeOtherException();
}
catch(...) //处理所有异常
{
//对异常作出响应(也许仅仅是部分的)
//...
throw; //将异常传给某个其它处理器
}