异常处理(exception handling)允许将异常检测和解决的过程分离开来,程序中某一个模块出现了异常不会导致整个程序无法正确运行。C++语言提供了异常内部处理机制,该处理机制涉及三个关键字:try、catch和throw。它们的功能分别是:检测可能产生异常的语句块、捕获异常和引发异常。
使用try和catch()捕获异常
要捕获语句可能发生的异常,可将它们放在try块中,并使用catch()块对try块可能引发的异常进行处理。
try
{
int* numArray = new int[0xffffffff]
delete[] numArray;
}
catch(std::bad_alloc& exp) //捕获new引发(throw)的std::bad_alloc类对象
{
cout << "Exception encountered: " << exp.what() << endl;
cout << "Go to end, sorry!" << endl;
}
catch(...) //捕获未被其他catch块显式捕获的所有异常。
{
cout << "Exception encountered. Go to end, sorry! "
}
使用trow引发异常
上述捕获std::bad_alloc时实际是捕获new引发的std::bad_alloc类对象。也可以引发自己选择的异常。
double divide(int a, int b) {
if(b == 0)
/*
1)当执行throw语句时,其后面的语句不会被执行。程序的控制权将转移到与之匹配
的catch模块。
2)throw可以抛出任意类型对象。通常情况下,抛出的异常为错误的编号、错误描述或用
户自定义的异常类对象。例如上面的代码抛出的异常为一个C风格字符串常量(类型为const char*)。
还可以抛出:
throw -1; //抛出一个整型数
throw x; //x为double类型对象
throw MyException("Fatal Error"); //MyException为一个类类型
*/
throw "Error, division by zero!";
return a / b;
}
int func()
{
try
{
cout << "Result is: " << divide(10, 0);
}
catch(const char* exp) //捕获类型为char*的异常
{
cout << "Exception: " << exp << endl;
cout << "sorry, can not continue!" << endl;
/* 如果在某函数的catch块中处理了捕获的异常,还想通知本函数的调用者让其继续处理,就可以
在catch块中指行“ throw; ”语句,这样的话就可以通知到本函数的调用者。*/
throw;
}
return 0;
}
每当使用throw引发异常时,编译器都将查找能处理该异常的catch(Type)。异常处理逻辑首先检查throw语句是否包含在try块中,如果是,则查找可处理这种异常的catch(Type)。如果throw语句不在try块内,或者没有与throw语句兼容的catch(),异常处理逻辑将继续在调用函数中寻找。因此,异常处理逻辑沿着调用栈向上逐个地在调用函数中寻找,直到找到可处理异常的catch(Type)。在退栈过程的每一步中,都将销毁当前函数的局部变量,这些局部变量的销毁顺序与创建顺序相反。
std::exception异常基类
上述捕获std::alloc时实际捕获new引发的std::alloc对象。std::bad_alloc继承了C++标准类std::exception(在<exception>头文件中声明)。像bad::cast(试图使用dynamic_cast转换错误类型(没有继承关系的类型)时引发)、ios_base::failure(由iostream库中的函数和方法引发)都是从std::exception派生而来的。
std::exception只定义了默认的构造函数、复制构造函数、复制运算符、虚析构函数和一个名为what的虚方法。可以继承exception类,并使用自定义版本的what成员。这个方法很有用,可以详细描述导致异常的原因。
由于std::exception是众多异常类型的基类,因此可以使用catch(const exception&)捕获所有将std::exception作为基类的异常。
try
{
...
}
catch(const std::exception& exp) //catch bas_alloc, bas_cast, etc
{
cout << exp.what() << endl;
}
从std::exception派生出自定义异常类(重点)
/*noexcept是C++11标准引入的新关键字,用来指明某个函数无法或不打算抛出异常。noexcept说明
必须放到形参列表后面,且函数的声明和定义处必须出现。如果是成员函数,则放置在const限定符之后,
在final、override或纯函数 =0之前*/
struct myExcetpion :public std::exception {
virtual const char* what()const noexcept {return "Ooops!";}
};
/*下面代码将抛出/引发一个myException异常对象,该对象可被异常声明为基类excetpion类型的catch子
句捕获*/
try {
throw myException();
}
catch(const exception &ex) {
cerr << ex.what() << endl;
}
note:
构造函数没有返回值,指出问题的唯一途径就是引发异常。
异常处理最后退栈的时候,如果函数局部变量有类对象,那么也将调用其析构函数。如果因出现异常而被调用的析构函数也引发异常,将导致应用程序终止。