常用的错误处理方式

  返回值:我们常用函数的返回值来标志成功或者失败,甚至是失败的原因。但是这种做法大的问题是如果调用者不主动检查返回值也是可以被编译器接受的,你也奈何不了他:) 这在C++中还导致另外一个问题,是重载函数不能只有不同的返回值,而有相同的参数表,因为如果调用者不检查返回值,则编译器会不知道应该调用哪个重载函数。当然这个问题与本文无关,我们暂且放下。只要谨记返回值可能被忽略的情况即可。

  全局状态标志。例如系统调用使用的errno。返回值不同的是,全局状态标志可以让函数的接口(返回值、参数表)被充分利用。函数在退出前应该设置这个全局变量的值为成功或者失败(包括原因),而与返回值一样,它隐含的要求调用者要在调用后检查这个标志,这种约束实在是同样软弱。全局变量还导致了另外一个问题,是多线程不安全:如果多个线程同时为一个全局变量赋值,则调用者在检查这个标志的时候一定会非常迷惑。如果希望线程安全,可以参照errno的解决办法,它是线程安全的。

  异常:现在我们再来看看异常能解决什么问题。对于返回值和errno遇到的尴尬,对异常来说基本上不存在,如果你不捕获(catch)程序中抛出的异常,默认行为是导致abort()被调用,程序被终止(core dump)。因此你的函数如果抛出了异常,这个函数的调用者或者调用者的调用者,也是在当前的call stack上,一定要有一个地方捕获这个异常。而对于setjmp()/longjmp()带来的栈上对象不被析构的问题对异常来说也是不存在的。那么它是否破坏了结构化(对于OO paradigms,也许应该说是破坏了流程?)呢?显然不是,有了异常之后你可以放心的只书写正确的逻辑,而将所有的错误处理归结到一个地方,这不是更好么?

  异常的优势:

  1、能够用较小的投入写出高质量的代码,省去了很多测试的时间。

  通常可以用try/catch/throw返回一个状态(有时也称为错误代码),调用者被假定为通过判定返回值确定函数是否执行成功。

  尽管返回技术有时候似乎是适当的错误处理技术,但是他也会增加一些令人厌恶的负面影响,例如:

  a)降低性能

  正如大家所知道的,相对其他判断而言,条件判断几乎10倍错误概率,因此,其他的事情相当于,如果你能把条件判定相关的代码移出你的工程,你很有可能会有一个相对健壮很多的代码。

  b)会延缓开发周期,提高成本

  会增加很多白鹤测试的测试用例,不必要的条件判定会增加很多的测试人力方面的开销;如果你没有测试过每一个代码分支,可能会有一些遗留bug,如果遗留给用户,将是非常严重的事情。

  c)增加开发成本

  发现bug,修改bug,测试bug将会增加很多不必要的流程控制复杂度。

  因此与用if判定错误代码相比,try/catch/throw会有少量的bug,相对便宜的开发成本,较短的开发周期,当然,如果你的团队没有使用exception相关的经验,你可以在一些预言性的项目上练习一下,以确保你知道怎么用它。

  2、构造函数调用失败后的处理

  构造函数没有返回值,因此不能返回错误代码,因此,用信号通知调用者调用失败的方法是抛出异常。

  3、析构函数调用失败后的处理

  写错误日至或者调用其他相关接口都可以,但是不要抛出异常 ,否则,如果该类中其他对象抛出了异常,在析构函数中再抛出异常,程序将terminate。