C++标准程序库由不同的成分构成。来源不同,设计与实现风格迥异。而错误处理和异常处理正是这种差异的一个典型体现。
1.1 标准异常类别
语言本身或者标准库所抛出的所有异常,都派生自积累exception.这是其它数个标准异常类别的基类,它们共同构成一个类体系具体如下:
这些标准异常可分为三组:{1.语言本身支持的异常;2.C++标准程序库发出的异常;3.程序作用域(scope of a program)之外发出的异常}
exception:
1)bad_alloc
2)bad_cast
3)bad_typied
4)logic_error {domain_error、invalid_argument、length_error、out_of_range}
5)ios_base::failure
6)runtime_error {range_error、overflow_error、underflow_error}
7)bad_exception
1.1.1 语言本身支持的异常
此类异常用以支撑某些语言特性,所以从某种角度来说它们不是标准程序库的一部分,而是核心语言的一部分。如果以下操作失败,就会抛出这类异常。
----全局操作符new操作失败,就会抛出bad_alloc异常(若采用new的nothrow版本,就另当别论)。由于这个异常可能于任何时间在任何复杂的程序中发生,所以可说是最重要的一个异常。
----执行期间,当一个加诸于reference身上的"动态性别转换操作"失败时,danamac_cast会抛出bad_cast异常。
----执行期型别辨识(RTTI)过程中,如果交给typeid的参数为零或空指针,typeid操作符会抛出bad_typeid异常。
----如果发生非预期的异常,bad_exception异常会接手处理,方式如下:当函数抛出异常规格(excepiton specification)以外的异常,bad_excepiton就会调用unexcepted()。例如:
class E1;
class E2;//not derived from E1
void f() throw(E1)//throws only excepiton of type E1
{
...
throw E1();//throws eception of type E1
...
throw E2();//calls unexcepted(),which calls
terminate()
}
f()之中抛出“型别为E2”的异常,这种动作违反了异常规则(exception specification)的设定,于是唤起了unexcepted(),后者通常唤起了terminate()终止程序。
然而如果在你的异常规则中列出bad_exception,那么unexcepted()纵使会重新抛出(rethrow)bad_exception异常。
class E1;
class E2;//not derived from E1
void f() throw(E1,std::bad_exception)//throws only excepiton of type E1 or
//bad_exception for any other exception type
{
...
throw E1();//throws eception of type E1
...
throw E2();//calls unexcepted(),which calls
bad_exception
}
1.1.2 C++标准程序库所发生的异常
C++标准程序库异常纵使派生自logic_error。从理论上讲,我们能够通过一些手段,从程序中避免逻辑错误。所谓逻辑错误包括违背逻辑前提或者违反class的不变性。C++ STL提供以下逻辑错误类别:
1)invalid_argument表示无效参数,例如讲bitset(array of bits)以char而非'0'或'1'进行初始化。
2)length_error指出某个行为"可能超越了最大极限",例如对着某个字符串附加太多字符。
3)out_range 指出参数值“不在预期范围之内”,例如在诸如array的容器或字符串(string)中采用一个错误索引。
4)domain_error指出专业领域范畴内的错误。
此外,标准程序库的I/O部分提供了一个ios_base::failure的特殊异常。当数据流(data stream)由错误或由到达文件尾端而发生状态改变时,就可能跑出这个异常。
1.1.3 程序作用域(scope of a program)之外发生的异常
派生自runtime_error的异常,用来指出“不在程序范围内,且不容易回避“的事件。C++标准程序库对执行期错误提供了一下三个classes:
1)range_error指出内部算术运算发生区间错误(range error)
2)overflow_error指出算术运算发生上溢位(overflow)
3)underflow_error指出算术运算发生下溢位(underflow)
【注】基础类别exception 和bad_exception定义于<exception>。bad_alloc定义于<new>
bad_cast 和bad_typeid定义于 <typeinfo>,ios_base::failure定义于 <ios>,其它异常类别定义于<stdexcept>。
1.2异常类别(exception class)的成员
为了catch字句中处理异常,必须采用异常所提供的借口。所有标准异常的接口只含一个成员函数:what(),用以获取"型别本身以外的附件信息"。它返回一个以null结束的字符串:
namespace std{
class exception{
public:
virtual const char *what() const throw();
...
};
}
what()返回的c-string在其所属的异常对象被摧毁后,就不再有效了。
标准异常的其它成员,用来处理生成、复制、销毁等动作。要注意的是,除了what()外,再没有任何异常提供任何其它成员函数,能够描述异常的种类。例如:没有可找出异常上下文(context)的一致性方法,或找出区间错误(range error)发生时的错误索引值。因此,唯一通过用的异常评估手段,大概只有打印一途了:
try{
...
}
catch(const std::exception& error){
//print implementation-defined error message
std::cerr<<error.what()<<std::endl;
...
}
1.3抛出标准异常
你可以在自己的程序库或程序内部抛出某些标准异常。允许你这般运用的各个标准异常,生成时都只需要一个string参数,它将被waht()返回的描述字符串。例如logic_error定义如下:
namespace std{
class logic_error :public exception{
public:
explicit logic_error(const string& whatString);
};
}
提供这种功能的标准异常有:logic_error及其派生类别、runtime_error及其派生类别、ios_base::failure.你不能抛出exception,也不能抛出任何用以支持语言核心性质的异常。
想要抛出一个标准异常,只需生成一个描述符异常的字符串,并将它初始化,交给异常对象;
std::string s;
...
throw std::out_of_range(s);
由char*可被隐式转换为string,所以你可以直接使用字符串字面量;
throw std::out_of_range("out_of_range(somewhere,somehow)");
1.4从标准异常类别(exception class)中派生新类别
另一个在程序中采用标准异常类别的可能情况是,定义一个直接或间接派生自exception的特定异常类别。要这么做,首先必须确保what()机制正常运作,what()是个虚拟函数,所以提供what()方法之一就是自己实现what();
namespace std MyLib{
/*user-defined exception class,
*derived from a standard class for exceptions
*/
class MyProblem : public std::exception{
public:
...
MyProblem(...);{//special constructor
}
virtual const char * what() const throw(){//what() function
...
}
};
...
void f(){
...
//create an exception object and throw it
throw MyProblem(...);
}
}
参考:The C++ Standard Library 侯捷 孟岩 译