所有被语言本身或标准库抛出的异常,都派生自基类 exception ,定义于 <exception> 。它是若干标准异常的基类,它们共同形成一个类体系,如图所示,
这些标准异常可被分为三组:
1:语言本身支持的异常。
2:C++标准库发出的异常。
3:程序作用域之外发出的异常。
逻辑错误通常可以避免,因为其错误发生在作用域内,例如先决条件被违反。运行期异常则是由一个位于程序作用域之外的原因触发,例如资源不足。
针对语言支持而设计的异常类:
此类异常用以支撑某些语言特性,所以,从某种角度说他们不是标准库的一部分,而是核心语言的一部分。例如以下操作如果失败就会抛出异常:
*运行期间,当一个 reference(引用)身上的“动态类型转换”失败时,dynamic_cast 会抛出 bad_cast 异常,此异常定义于 <typeinfo>。
*运行期类型辨识(RTTI)过程中,如果交给 typeid 操作符的实参为 0或为空指针,typeid 操作符会抛出 bad_typeid 异常,此异常定义于<typeinfo>.
*定义于<exception> 内的bad_exception 异常用来处理非预期的异常。它可以由函数 unexpected() 抛出,该函数会在“某个函数抛出的异常不在异常明细内”时被调用。然而请注意,自C++11起,已经不再提倡使用异常明细。
这些异常也可以被程序库函数抛出。例如:bad_cast 有可能由 use_facet<>抛出,如果一个facet 在某个 local 内不可用的话。
针对逻辑差错而设计的类。
针对逻辑差错而设计的异常总是派生自 logic_error 。所谓逻辑差错就是可在程序中避免的错误,例如通过“对函数实参进行额外测试”而避免出现逻辑差错。这类错误包含如“违背逻辑先决条件”或“违反 class不变性”等。C++标准库提供了以下五种针对逻辑差错的异常类:
*invalid_argument 表示无效实参,例如将 bitset(array of bits)以char 而非‘0’或‘1’进行初始化。
*length_error 指出某个行为“可能超越最大容许大小”,例如在类似 array 的集合或字符串(string)中采用一个错误索引。
*domain_error 指出领域范畴内的错误。
*自C++11起,future_error 用来指出当使用非同步系统调用时发生的逻辑差错。此范围内的运行期差错乃是借由 class system_error。
一般而言,针对逻辑差错而设计的异常类往往被定义于<stdexcept>内,不过 class future_error却是被定义于<future>。
针对运行期差错而设计的异常类
派生自 runtime_error 的异常都被用来指出“不在程序作用域内且不容易回避”的事件。
C++标准库为此提供了以下几个class:
*range_error 指出内部计算时发生的区间错误。自C++11起,C++标准库有可能在“wide sring 和 byte string之间转换”时发生这个异常。
*underflow_error指出算数运算发生上溢。C++标准库中,如果一个 bitset 被转换为一个整数值,就会抛出这个异常。
*underflow_error指出算数运算发生下溢。
*自C++11起,system_error用来指出因底层操作系统而发生的差错。C++标准库有可能在并发环境中抛出异常。例如:class thread ,用以控制data race 的那些类以及 async() 。
*只要全局操作符 new 失败,定义于<new>的bad_alloc就会抛出异常,除非用的时new的 nothrow版本。由于这个异常可能于任何时间在任何较复杂的程序中发生,所以可认为是一个最重要的异常。
自C++11起,派生自bad_alloc的bad_array_new_length会被new抛出——如果传给new的大小小于0或超过编译器定义的极限(也就是说,那就是个逻辑问题而不是一个运行时期错误)。
*当“根据一个shared pointer创建一个weak pointer”的操作失败,定于于<memory>中的bad_weak_ptr会被抛出。
*当一个function 外覆物被调用但其实际没有目标时,定义于<functional>中的bad_function_call会被抛出。
此外,针对标准库中的 I/O部分,有一个特别的异常类 ios_base::failure被定义于<ios>。此类异常可能会在 stream 由于发生错误或遇上 end-of-file 而改变其状态时被抛出。自C++11起这个类派生自 system_error,在C++11之前它直接派生自 exception。
从概念上来说,bad_alloc可被视为属于一种 system error。然而由于历史原因,也由于其重要性,当编译器想要表达“内存不足“时,多半会抛出 bad_alloc而不是system_error。
一般而言,针对运行期差错而设计的class被定义于<stdexcept>内。但system_error却被定义于<system_error>内。
由标准库抛出的异常
如上所述,几乎所有的异常类都有可能被C++标准库抛出。尤其是,无论何时分配储存空间,都有可能抛出 bad_alloc异常。
此外,由于标准库可能用到引用程序开发人员所写代码,所以也可以间接掷出任何异常。
标准库的任何具体实现都有坑能提供更多异常类(或为兄弟类或派生为子类)。然而使用这些非标准类将导致程序难以移植,因为一旦你想采用其它标准库实现,就不得不修改程序。所以,最好只使用标准异常类。
异常类的头文件
异常类定义于许多头文件中。为了能够处理标准库可能抛出的异常,必须包含:
#include<exception>
#include<stdexcept>
#include<system_error>
#include<new>
#include<ios>
#include<future>
#include<typeinfo>