异常处理机制主要有两个主要成分:异常的鉴定与发出,以及异常的处理方式。通常,不论是member function或non-member function,都有可能产生异常以及处理异常。异常出现之后,正常程序的执行便被暂停(suspended),与此同时,异常处理机制开始搜索程序中有能力处理这一异常的地点。异常被处理完毕之后,程序的执行便会继续(resume),从异常处理点接着执行下去。
1、异常抛出(Throwing an Exception)
C++通过throw表达式产生(抛出)异常:
void divide(int x, int y)
{
if(y == 0)
{
throw x; // 抛出(int)类型的异常
}
cout << "divide结果:" << x/y << endl;
}
如上面程序中,如果y==0,就会抛出一个int类型的异常,当执行完throw语句之后,下面的cout语句便不会再被执行。
throw表达式看起来有点像函数调用 。那么何谓抛出(thrown)一个异常?所谓异常(Exception)是某种对象。最简单的异常对象可以设计为整数或者字符串:
throw 42;
throw "there's is an exception";
但是大部分时候,被抛出的异常都属于特定的异常类(也许形成了一个继承体系)
class it_overflow
{
public:
it_overflow(int index, string reason):
_index(index),
_reason(reason){}
void what_happened()
{
cout << "Internal error:current index: "
<< _index << " reason: " << _reason << endl;
}
private:
int _index;
string _reason;
};
如上,定了一个异常类,异常类的定义和普通类的定义没有什么不同。那自定义异常类是怎么抛出呢?如下所示:
void play05()
{
int currIndex = 100;
int maxIndex = 10;
if(currIndex > maxIndex)
{
throw it_overflow(0, "the currIndex greater than maxIndex!");
}
}
如上所示,当发生错误时,会直接抛出一个it_overflow异常类的一个匿名对象,当然我们也可以明确指出抛出的对象的名称,如下所示:
void play05()
{
int currIndex = 100;
int maxIndex = 10;
if(currIndex > maxIndex)
{
it_overflow ex(0, "the currIndex greater than maxIndex!");
throw ex;
}
}
2、异常捕获(Catching an Exception)
C++中可以利用单条或一连串的catch字句看来捕获(catch)被抛出来的异常对象。catch字句由3部分组成:关键字catch,小括号内的一个类型或对象、大括号内的一组语句(用于处理异常)。
catch字句应该和try块相应而生。try块是以关键字try作为开始,然后大括号内的一连串程序语句。catch字句放在try块的末尾。这表示,如果try块内有任何异常发生,便由接下来的catch字句加以处理。如下所示:
void main()
{
try
{
play05_1();
}
catch(it_overflow& e) //最好写成by reference,如果是by value就需要拷贝构造函数
{
e.what_happened();
}
catch(int err)
{
cout << "Exception:" << errno << endl;
}
catch(const char* str)
{
cout << "Exception:" << str << endl;
}
catch(...)
{
cout << "未知异常!" << endl;
}
return;
}
上述程序定义了一个try块和四个catch字句,能够捕获三种类型的异常对象,其中catch(...)用于捕获未知类型的异常对象,即当抛出的异常没有catch字句进行捕获时,就会进入到catch(...)字句内。
上述的catch字句分别处理以下所抛出的三个异常对象:
throw it_overflow(0, "the currIndex greater than maxIndex!");
throw 42;
throw "there's is an exception";
需要注意的是,抛出的异常,如果在程序中没有对应的catch字句进行捕获并处理的情况下,程序会依照函数的“函数的调用链”一一向上传递,知道找到能够处理异常的catch字句,如果一直回到了main()函数还是找不到合适的catch字句,那会发生什么事呢?C++规定,每隔一场都应该被处理,因此,如果main函数内还是找不到合适的处理程序,便会调用标准库提供的terminate()--其默认行为是终端整个程序的运行。
3、标准异常(The Standard Exceptions)
标准库定义了一套异常类体系(Exception class hierarchy),其根部是名为eception的抽象基类。在exception声明有一个what()虚函数,会返回一个const char*,用以表示被抛出异常的文字描述。在c++中,所有抛出的异常,都可以用eception类型进行捕获:
catch(const exception& ex)
{
cout << ex.what() << endl;
}
这样当处理抛出的标准异常时,只需要写一个ctach字句就可以了。
当然,我们也可以自定义一个继承自exception基类的异常类,如下所示:
Code3-2:
#include <iostream>
#include <string>
#include <sstream> //for ostringstream
#include <exception> // for exception
using namespace std;
class myException: public exception
{
public:
myException(int index, string reason):
_index(index),
_reason(reason){}
//overrides exception::what()
const char* what() const;
private:
int _index;
string _reason;
};
const char* myException::what() const
{
ostringstream ex_msg;
//将输出信息写到内存内的 ostringstream 对象之中。
//将整数值转化为字符串表示...
ex_msg << "Internal error:current index: "
<< _index << " reason: " << _reason << endl;
static string str = ex_msg.str(); //需要用static变量接收,要不然会造成程序执行结束,局部变量的内存就销毁了
return str.c_str();
}
继承自exception基类,必须要定义what()函数!在捕获异常时,就可以直接捕获exception类型的异常对象就可以了:
//throw
myException ex(0, "the currIndex greater than maxIndex!");
throw ex;
//catch
catch(exception& ex)
{
cout << ex.what();
}
4、自定义异常类型
自定义异常类型和3章节基本上内容差不多,只不过异常的抽象基类是自定义的,比如定义了一个自定义的异常抽象基类:myAbsException。那其他所有的异常类型对象都继承自该基类,并需要实现定义的额纯虚函数。
在捕获的时候也就只需要捕获myAbsException类型的异常对象就可以了,具体代码可参考 Code 3-2。