C++进阶-异常处理

异常处理

异常处理使用try关键字来尝试执行可能会出现异常的代码段,当在代码段的执行过程中有异常发生时,系统会抛出相应类型的异常,由紧随其后的catch关键字对异常进行捕获,并交由相应类型的异常处理分支对其进行恰当的处理,比如结束正在执行的操作、清理不再需要的资源等,从而尽可能地挽回用户的损失。

// 用 try 开始异常处理语句
try
{
	// 包含可能发生异常的语句
}
catch(异常类型 [形参名]) // 捕获特定类型的异常
{
	// 对此类型异常进行处理
}
catch(异常类型 [形参名]) // 捕获特定类型的异常
{
	// 对此类型异常进行处理
}
// 可以有多个 catch 语句并列,捕获不同类型的异常
catch(...) // 如果省略具体的异常类型用“...”表示,则表示捕获所有类型的异常
{
	// 对所有类型的异常进行处理
}

使用throw关键字抛出一个相应类型的异常,表示某种异常情况的发生,需要后面的 catch 语句捕获并对其进行处理。用 throw 关键字抛出一个异常的语法格式如下:

throw 异常表达式;

异常表达式就是要抛出的异常,它可以是表示异常类型的错误代码,或者是含有异常相关信息的某个对象,总之,它的意义是为异常处理提供相应的辅助信息。

// 除法函数
double Divide( int a, int b )
{
if( 0 == b )
throw "不能使用 0 作为除数";
return (double)a/b;
}

当 throw关键字抛出的异常被某个 catch 语句捕获时, throw 关键字后的异常表达式会被当成实际参数传递给catch 语句中的形式参数,进而 catch 语句可以根据这个参数提供的信息对异常进行具体的处理。当有多种类型的异常需要捕获时,可以将多个 catch 语句并列。如果省略 catch 关键字后面的形式参数而使用“…”代替,则表示 catch 语句会捕获所有类型的异常。

// 开始异常处理语句 
try
{
	cout<<"请输入被除数与除数: "<<endl;
	int a,b; // 被除数与除数
	cin>>a>>b; // 接收用户输入
	// 进行除法运算,当 b 为 0 时会抛出异常
	double fRes = Divide( a, b );
	cout<<a<<"/"<<b<<" = "<<fRes<<endl; // 输出结果
}
// 捕获 try 语句块中所抛出的字符串类型异常
catch( char* pMsg )
{
	// 对异常进行处理
	// 这里仅仅是输出错误信息
	cout<<"程序运行发生异常: "<<pMsg<<endl;
}

在函数声明之后用 throw 关键字加一个括号,括号中列出这个函数可能抛出的异常类型,如果有多个类型的异常,可以用逗号间隔。
例如, Divide()函数可能会抛出字符串类型和浮点数类型的类型,则可以将其定义如下:

// 可能抛出 char*和 double 类型的异常
double Divide( int a, int b ) throw ( const char*, double )
{
// …
}

如果在函数的声明中没有包括异常的接口声明,则此函数可以抛出任何类型的异常。例如:

// 可以抛出任何类型的异常
double Divide( int a, int b );

相反,如果一个函数不会抛出异常,那么我们可以将函数声明中 throw 关键字之后的异常类型列表留空,或者是用 noexcept 关键字对函数进行修饰,表示这个函数不会抛出任何类型的异常,函数的使用者无需将其放到 try 语句块中来执行。例如:

// 不会抛出任何类型的异常
double Divide( int a, int b ) throw();double sqrt(double a) noexcept;

如果一个经过 noexcept 修饰的函数确实在执行的时候抛出了异常,那么程序会通过调用
terminate()函数来结束自己的执行。这样做可能会留下很多后遗症,例如,它无法保证对象的析构函数的正常调用,也无法保证栈的自动释放等。但是,在可以预见异常极少发生的情况下,将函数用 noexcept 修饰却是一种简单而直接,却非常有效的异常处理机制,它可以简化异常处理从而在一定程度上提高程序的执行效率。

上一篇:c++中try catch的用法


下一篇:c-为什么要按基数对派生类进行捕获?