C++错误处理

常言道,要学打架,先学挨打。想打人,就要有被人打的觉悟。写程序,自然也要有程序运行出错的觉悟。游戏编程,就从错误处理开始。

什么是错误处理?我想应该就是发现错误、解决错误的过程。什么是错误?比如我调用某个函数,那总会有一个期望的结果。但在实际运行的时候,有可能与期望的结果相符合,也有可能与期望的结果不符合。后面一种情况,就算作是“错误”。一个函数要么有报告错误的责任,要么确保自己不会出现错误(没有绝对的不出错,但有些函数比如std::swap等,除了堆栈溢出、硬件故障之外几乎没有出错的可能了,因此认为它不会出错。但凡是涉及到new操作的,由于内存分配可能失败,所以都是有可能出错的)。如果两者都没有,那我们的程序就危险了。

听别人的总结,报告错误有三种方法。一是函数直接返回错误代码;二是把错误代码保存起来,让外界用GetLastError之类的函数去获取;三是抛出一个异常。我发现其实还有第四种办法:传入一个回调函数,在错误时调用它。

举例:COM风格的代码喜欢用HRESULT来表示各种错误。Windows API中的多数函数,以及OpenGL都喜欢GetLastError这样的方式。C++标准的new则又用异常来表示内存分配失败。C++还有一个函数set_unexcepted,在产生未声明的异常时调用回调函数。可见,四种方法在C/C++的经典代码中都有出现,最后搞得不知道该用哪种好了。

从性能上讲,抛出异常的开销似乎要大一点,但我这里说“似乎”大一点,这个问题我不确定。异常处理有代价,错误代码的判断同样有代价,两种代价孰轻孰重,我想或许还要看看调用堆栈的深度。每次调用函数都去判断错误代码的话,效率也不见得高(调用层次很深的话,层层判断是很麻烦的。并且大量判断会对CPU指令流水线造成压力)。

错误代码和GetLastError两种方式比较相似,都是需要在函数返回之后进行判断。有人说GetLastError会存在线程安全问题,不过其实这可以用TLS(线程局部存储)去解决。但是如果不小心,函数的返回值被忽略了,则错误可能不会被及时处理,并且编译器不会发出任何有用的警告。

回调函数的性能看起来是最高的,但是它也不像想象的那么好——它的控制权太弱了。其它三种方式都是函数返回之后再进行处理,程序控制要灵活得多。但是回调函数的方式,发生错误后进入回调函数时,产生错误的那个函数并没有返回。此时要再想跳转到别的位置去执行代码,总会觉得力不从心。

或许应该更倾向于用异常来报告错误。原因很简单:代码简短(判断语句少),这个理由就足够了。据说使用异常的代码也比使用返回值的代码更容易进行白盒测试。另外,异常不像返回值那样容易被忽略,也不像回调函数那样控制力不足。如果从JAVA来看的话,采用返回值来报告错误的设计貌似已经不多见了。看来在不考虑性能的情况下,使用异常来报告错误应该是目前的最佳选择。(前面也说了,即使考虑性能,估计仍然是最佳选择)

由于C++不是自动垃圾回收的,在异常机制面前,资源的分配释放显得尤为重要。异常安全是值得考虑的问题(搜索一下“异常安全”)。尽可能的使用RAII的方法会有帮助。

此外还有一个问题,就是链接。假设两个模块采用不同的编译器(或者相同的编译器,但是设置不一样),则一个模块所抛出的异常可能无法被另一个模块所识别。为此,需要确保程序的所有模块都采用相同编译器、相同设置去编译。这在一定程度上限制了使用范围。

虽然推荐使用异常来报告错误,但也要设法在一定程度上减少异常的数量,实际上就是减少错误的出现机会。举例来说,走路的时候,如果前方可能有个坑,应该怎么办呢?方案1:走一步试试,有坑就抛出异常,没坑就啥事儿没有。方案2:先仔细查看是否真的有坑,然后决定是否要踏进去。选择哪种方案取决于“坑存在的可能性”。如果坑存在的可能性较大,则方案1就是不明智的(如果真的踩中了坑,则代价惨重)。如果坑存在的可能性小,则方案2就是不明智的(瞻前顾后,畏首畏尾)。需要根据情况选择合适的处理方式。

小结:1. 使用异常来报告错误。2. 注意异常安全,尽可能的使用RAII。3. 如果某个错误发生的机率较高,则尽可能进行主动的检查,而不要让异常频繁的被抛出。“异常仅仅在异常情况下才出现”。

说明一下,此文拷贝自http://hi.baidu.com/zhlz01/item/eab08bcce2699d1cb77a2427

原文的作者就是我本人,因此算作是原创,不是转载。这是我在2010年时候写的一篇老文。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值