转载自:http://blog.csdn.net/sadjason/article/details/22225231
C++标准库由不同的成分构成。来源不同(不是由某个人或某个组织以某种统一的形式弄成的),设计与实现风格迥异。而错误处理和异常处理正是这种差异的一个典型代表。标准程序库中有一部分,例如string classes,支持具体的错误处理,它们检查所有可能发生的错误,并于错误发生时抛出异常。至于其他的部分如STL和valarrays,效率重于安全,因此几乎不检测逻辑错误,并且只在执行期(runtime)发生错误时才抛出异常。
有哪些异常?
语言本身或标准程序库所抛出的所有异常都派生自exception,主要包括三类:
a. C++语言本身所支持的异常(也即不管是string还是STL,只要是C++代码都可能会出现的异常);
bad_alloc、bad_cast、bad_typeid、bad_exception
b. C++标准库所所发出的异常
ios_base::failure、logic_error、domain_error、invalid_argument、length_error、out_of_range
P.S:除了ios_base::failure和logic_error是平级之外,logic_error是其他几个异常的基类;
c. 程序作用域之外发出的异常
runtime_exception、range_error、overflow_error、underflow_error
假设有一个getData()函数,它的作用是返回某种内置数据类型,它可能是以下某种定义方式:
char getData(...) { ... } // char型返回值 int * getData( ... ) { ... } // int *型返回值 int getData( ... ) { ... } // int型返回值
这个函数可能是通过计算进而得到某个结果进行返回,可能是从某个地方获取一个值把它返回,前者可能会发生计算错误,后者可能发生取值错误(这个可能性就比较高了),比如指定地址的值不存在等等。无论如何,函数需要把操作结果告诉用户(函数的使用者),即用户需要通过某种途径知道此函数是否成功执行了 。
若返回值是char类型,可以返回‘\0’来表示“失败”(其实也不见得是个好的方式),因为有效字符不会是‘\0’值;
若返回值是指针类型,可以返回NULL来表示“失败”,因为有效指针不会是NULL;
但若返回类型是int类型呢?该返回什么值呢?事实上,对于int类型来说,没有任何值是无效的。但总要解决这个问题啊!
这时异常就可以解决这个问题。对于带有返回值的函数,结束函数的运行,或者通过return语句结束函数,或者通过以抛出异常结束函数。
所以 ,上述int型返回值的getData函数可以如下这么写:
这里插一个概念 -- 用户:比如在A函数中调用B函数,对于B函数来说,A就是用户。
总之,在C中很多场合,函数可以执行“return NULL;”告诉它的用户“得不到正常的结果”,但有些场合“return xxoo;”无法解决问题,比如上述“int getData(...) { ... }”,在C++中,异常可以解决这个问题,不过用的不是return语句,而是throw语句;用户也不是通过“if (result == NULL)”之类的语句进行判断,而是调用“try-catch”语句。
但异常的作用显然不是这么简单 -- “告诉用户得不到正常的结果”。对于C语言,若执行如下语句:
int *p = new int; delete p; delete p;
程序只能崩溃,而C++若使用合适的异常处理这个问题,可以避免程序崩溃,反正异常机制能干这么一种事情,关于如何实现,比较简单,但不是本文的重点,这里引出这个东东只是为了说明异常机制的重要性。
上面都是小case,开始一些稍微有意思的东西吧!
从问题开始:
1. 异常的本质是啥?有啥意义?
异常的本质:内容上文所提到的异常,比如bad_alloc其实是一个类,所以使用throw抛出异常时不能直接执行“throw bad_alloc;”这样的语句,而是先要创建一个bad_alloc对象,所以至少得这样“throw bad_alloc()”;
异常的意义:首先得明白一点,和NULL有一丁点类似,异常是一种通知用户“程序没能正常执行”或“得不到想要的结果”之类的机制,有点类似于函数和它的用户之间的一个约定!
异常机制能修复程序出现的问题吗?No,异常其实是一个非常非常简单的类,它不会对程序做任何实质性的改变,笔者认为从某种角度可以把异常看做C++定义的一种类似于NULL但比NULL要丰富得多的MARK,比如执行new语句失败了,系统会throw一个bad_alloc,用户(调用该new语句的程序)可以根据此错误做些其他事情(比如再执行一次之类的),总之,如何处理这些MAKR,全看用户的catch语句如何处理。
2. 如何抛出异常?
异常对象可能在程序执行期间产生异常时被系统抛出,也可能是某些程序显式执行“throw xxoo;”语句抛出给用户。
P.S:笔者对此并不确定,貌似Java中虚拟机可以抛出异常(可以认为是所谓的“系统”吧),但对于C++,笔者不太确定!或许以后会搞清楚!
3. 异常可以处理所有错误吗?
No,如下:
编译执行此程序仍然会发生错误,“sb”仍然无法被打印出来。
4. 关于自定义异常
当然可以自定义异常,public继承exception就可以了。
但是尽量不要自定义异常,《C++标准程序库》是这么说的:
使用非标准类别作为异常将导致程序难以移植,xxoo,所以最好使用标准异常。
但笔者认为,有时候自定义异常还是挺必要的,比如,上文所提到的“int getData(...) { ... }”,若得不到想要的结果时,return语句显然不满足,只能使用throw,但是throw已知的标准异常类对象是不太合适的,因为它们都有特别的意义,使用它们会给用户带来困惑,所以笔者认为此时需要自定义异常。
所有标准异常类的接口只含一个成员函数:what(),用以获取“type本身以外的附加信息”。它返回一个以null结束的字符串:
-
namespace std{ class exception { public: virtual const char * what() const throw(); ... }; }
5. 异常规格(exception specification)
函数可以随意返回各种异常吗?No,函数若想返回某个异常,需要在其声明式和定义式中添加这么一个东西(见蓝色部分)
void f() throw (bad_alloc, bad_cast); void f() throw (bad_alloc, bad_cast) { throw bad_cast(); throw bad_alloc(); }
所谓异常规格,用来指明某个函数可能抛出哪些异常。
若函数抛出了没在异常规格中出现的异常会怎如何呢?
《C++标准程序库》中这么说:唤醒unexpected(),后者会唤醒terminated()终止程序。
所以,如下程序:
void f() throw (bad_alloc) { throw bad_cast(); throw bad_alloc(); } int main(int argc, char **argv) { try { f(); } catch(const bad_cast& e) { } catch(const bad_alloc& e) { } cout << "hello, world" << endl; }
执行结果如下:
libc++abi.dylib: terminating with unexpected exception of type std::bad_cast: std::bad_cast
[1] 3932 abort ./a.out
“hello, world”还没来得及打印就退出了!
然而如果在异常规格中列出bad_exception,那么unexpected()总是会重新抛出(rethrows)bad_exception。
-
class E1; class E2; // not derived from E1 void f() throw(E1, std::bad_exception) // throws exception of type E1 or // bad_exception for any other exception type { ... throw E1(); // throws exception of type E1 ... throw E2(); // throws unexpected(), which throws bad_exception }
因此,如果异常规格罗列了bad_exception,那么任何未列于规格的异常,都将在函数unexpected()中被代之以bad_exception。
7.异常的处理机制(补充)
- 若有异常则通过throw操作创建一个异常对象并抛出。
- 将可能抛出异常的程序段放在try块之中。控制通过正常顺序执行到达try块,然后执行try子块内的保护段。
- 如果在保护段执行期间没有引发异常,那么跟在try子块后的catch字句就不执行。程序继续执行紧跟try块最后一个catch子句后面的语句。
- catch子句按其在try块后出现的顺序被检查。类型匹配的catch子句将被捕获并处理异常(或重抛出异常)
- 如果找不到匹配的处理代码,则自动调用terminate,默认为abort()终止程序。
void f() throw (bad_alloc, bad_cast);
void f() throw (bad_alloc, bad_cast)
{
throw bad_cast();
throw bad_alloc();
}