异常处理提供了一种对程序中的异常情况(如运行时错误)作出反应的方法, 该方法是将异常转移到称为异常处理程序的特殊函数。
为了捕获异常,将代码的一部分置于异常检查之下。这可以通过将这部分代码封装在try-block中来实现。 当该块中出现异常情况时,抛出一个异常,将控制权转移到异常处理程序。 如果没有抛出异常,代码将正常继续,并忽略所有处理程序。
在try语句块中使用throw关键字抛出异常。异常处理程序是用关键字catch来声明的,它必须放在try块的后面:
// exceptions #include <iostream> using namespace std; int main () { try { throw 20; } catch (int e) { cout << "An exception occurred. Exception Nr. " << e << '\n'; } return 0; } |
An exception occurred. Exception Nr. 20 |
异常处理下的代码封装在一个try块中。在这个例子中,这段代码只是抛出一个异常:
try { // code here } catch (int param) { cout << "int exception"; } catch (char param) { cout << "char exception"; } catch (...) { cout << "default exception"; } |
在这种情况下,最后一个处理程序将捕获抛出不是int或char的任何类型的异常。
在程序处理了异常之后,在try-catch块之后继续执行,而不是在throw语句之后!
也可以在更多外部try块中嵌套try-catch块。 在这些情况下,内部catch块有可能将异常转发到其外部级别。 这是通过表达式throw完成的;不带参数。例如:
try { try { // code here } catch (int n) { throw; } } catch (...) { cout << "Exception occurred"; } |
旧的代码可能包含动态异常规范。它们现在已被c++弃用,但仍受支持。 动态异常说明紧跟在函数声明之后,并附加抛出说明符。例如:
double myfunction (char param) throw (int); |
这声明了一个名为myfunction的函数,该函数接受一个char类型的参数并返回一个double类型的值。 如果该函数抛出非int类型的异常,则调用std::unexpected函数, 而不是寻找处理程序或调用std::terminate函数。
如果这个throw 为空且没有类型,这意味着任何异常都会调用std::unexpected。 没有throw 的函数(常规函数)永远不会调用std::unexpected,而是遵循查找异常处理程序的正常路径。
int myfunction (int param) throw(); // all exceptions call unexpected int myfunction (int param); // normal exception handling |
c++标准库提供了一个基类,专门用于声明要作为异常抛出的对象。 它被称为std::exception,定义在<exception>头文件中。该类有一个名为what的虚成员函数, 该函数返回一个以空结束的字符序列(类型为char *), 可以在派生类中重写该函数以包含对异常的某种描述。
// using standard exceptions #include <iostream> #include <exception> using namespace std; class myexception: public exception { virtual const char* what() const throw() { return "My exception happened"; } } myex; int main () { try { throw myex; } catch (exception& e) { cout << e.what() << '\n'; } return 0; } |
My exception happened. |
我们放置了一个通过引用捕获异常对象的处理程序(注意类型后面的&号), 因此它也捕获来自exception的类,比如myexception类型的myex对象。
所有由c++标准库的组件抛出的异常都来自于这个异常类。这些都是:
异常 | 描述 |
bad_alloc | 由new抛出的分配失败 |
bad_cast | 当动态类型转换失败时由dynamic_cast抛出 |
bad_exception | 由某些动态异常说明符引发 |
bad_typeid | typeid抛出 |
bad_function_call | 由空函数对象抛出 |
bad_weak_ptr | 当传递一个错误的weak_ptr时由shared_ptr抛出 |
同样从exception派生的头文件<exception>定义了两种泛型异常类型,可以被自定义异常继承来报告错误:
异常 | 描述 |
logic_error | 与程序内部逻辑有关的错误 |
runtime_error | 运行时检测到错误 |
需要检查标准异常的典型例子是内存分配:
// bad_alloc standard exception #include <iostream> #include <exception> using namespace std; int main () { try { int* myarray= new int[1000]; } catch (exception& e) { cout << "Standard exception: " << e.what() << endl; } return 0; } |
在这个例子中,异常处理程序可能捕捉到的异常是bad_alloc。 因为bad_alloc派生于标准基类异常,所以可以捕获它(通过引用捕获,捕获所有相关的类)。