我是
靠谱客的博主
辛勤夕阳,最近开发中收集的这篇文章主要介绍
C++错误处理方式的思考,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
前言:
之前在学习和工作一直考虑异常机制和错误处理之间的关系,在看了一些资料和阅读了一些文章之后,有了几点想法。本文的大部分内容来自于下面几篇文章的总结和思考:
比较系统的介绍了错误处理的相关问题:
http://blog.csdn.net/pongba/article/details/1815742
介绍了异常机制在项目中的应用,同时介绍了ScopeExit和用无限展开宏技术写的一个errert
:http://mindhacks.cn/2012/08/27/modern-cpp-practices/
知乎上中对异常的讨论:
https://www.zhihu.com/question/22889420
一.错误处理要思考的四个问题:
1.错误的定义,即什么是错误;
2.如何报告错误?error-code机制还是异常机制?
3.何时,如何处理错误
二.什么是错误
错误即是当程序执行到某处时,不应该出现的情况,在debug版本中,你通常会用assert()去处理的点,你相信此处不会出现意料之外的事情,但却偏偏发生了。一些最常见的例子:调
用方传入的参数不满足被调用方的precondition;你要打开的文件找不到或者是没有权限打开;数据库访问出错,网络传输出错,资源获取失败等等。
这些例子都有一个共同的特点就是,会导致程序无法继续正常的执行下去,或者不知如何转到其他的执行路径中:
A.调用方传入的参数无法满足被调用方的preconditin,例如会导致数组越界,这时是肯定无法正常的执行程序了,只能是报告该错误,让程序处于一个能暴露该错误的状态,例如强制退出。
B.资源获取失败,例如new()失败了,这时还能怎么处理?肯定是最善后清理工作,然后让程序退出了。
C.数据库访问出错,网络传输出错,不一定会导致整个程序失败,但至少某部分的功能是不能正常的使用了。
这些例子都说明了错误的显著特征,这是和普通的状态码标识的某些状态区分开来的。因为在用error-code的时候,很多人都把函数的返回值误以为是error-code,其实不然,很多返回值应该叫status-code,它只是用来表示程序的不同执行状态,通常用来作为程序控制流使用:
if(foo())
{
path1
}
else
{
path2
}
error-code和status-code都是使用同样的一个信道,即函数的返回值,这才导致了很多人没有注意到应该把这两个东西区分开来。之所以特意提到这一点,是想强调,错误处理机制是用来汇报错误的,而不是用来控制程序流的。当想把一个系统的错误处理机制从error-code换成异常机制时,特别需要注意这一点。很多人把error-code和status-code都用异常来替换,然后就导致了 throw,try,catch遍布各处,然后就抱怨C++的异常机制有各种问题:资源泄露,事务语意难以保证等异常安全问题。根本问题就是异常机制被乱用了而已。
要处理好错误本质上来说都是困难的,而跟使用error-code还是异常机制无关,我们无非是想讨论一些在哪些情况下用哪种机制会相对方便一点而已。
三.如何报告错误
error-code还是异常?这是很多C++程序员争论的点。error-code的特点:
1.用函数返回值作为传递错误的信道,兼容C的接口
2.如果需要将错误往上层传递时,需要一层层的检查函数的返回值,显式传递给上一层;否则的话,将会带着错误状态往下执行程序流,可能会在接下来的执行流的某处,程序就莫名奇妙的爆掉了,这时离原始的错误点已经很远,带来的代价是很难找到错误的根本原因了。一阶错误没处理好将导致二阶三阶问题的出现。
3.从错误发生点到最终的错误处理点的需要一层层通过return返回。这种方式的优点是,在工程中或在code-review的时候,更容易发现错误安全的问题,例如函数退出时有没有释放掉之前申请的资源。但是缺点也很明显,首先是繁琐,需在每一层函数中写错误检查代码;第二是如果在某一层忘了写错误检查代码,则错误有可能被忽略掉了。
异常机制的特点:
1.单独的错误汇报信道,并且更容易携带更丰富的信息,不兼容C的接口。
2.错误信息不会被忽略,会自动的一层层往上传递直到被catch。优点是更容易将错误传递到上层进行统一处理;但是如果没有做好资源的自动管理,则很难做好资源泄露问题,因为函数的退出是隐式的。
3.在构造函数或者其他一些返回值固定的函数,例如[]操作符重载中,更容易汇报错误
4.代码中不需要很多错误检查的代码,而是只有真正的功能代码,减少噪音。
我个人的看法是,一个系统中,如果错误发生后,能够立即被处理或者在比较近的层次中被处理,而无需被传到上层,那可以用error-code的方式;
如果错误发生后并不能被立即处理,而是只有比较上层的层次才知道如何处理错误,那么就应该用异常机制,这不仅是考虑到错误能够更方便的被传递,少写一些代码,更大的原因是错误不会在传递中被忽略掉。在这种情况下,因为错误发生点在调用层的深处,程序员可能就会懒得每一层都进行检查了。
当然,如果用了异常机制,那么应该配合用RAII手法进行资源管理。不管是用error-code的方式还是用异常机制,都会有错误安全的问题,这是错误处理的本质问题。但是有一个区别就是,一旦用了异常机制,毫无疑问的只有用了RAII手法才能减轻资源管理的负担。(不止内存资源,还包括数据库连接,文件句柄等资源)
四.何时,如何处理错误
这个问题首先应该讲错误分成两类:可恢复的错误和不可恢复的错误。
对于可恢复错误:
(1)在发生点立即恢复错误,就像错误从来没发生一样。例如,配置文件缺失,则创建一个缺省配置值;资源获取失败,启用备用资源等等;这类错误恢复的关键点是当前的函数有足够的环境上下文,知道恢复错误所需的所有数据。
(2)回滚到上层函数栈中进行恢复。这种错误回滚一般在某个层次将整个函数栈的执行看成一组事务,如果错误则整组事务撤销,恢复数据或者引导程序到一条备用路径上执行。
对于不可恢复错误:
这时就没办法了,只能退出当前模块或者程序挂掉了。在退出模块之前必须进行关键数据的保存,申请资源的释放(包括网络端口,数据库链接等资源),日志的记录等善后工作。而且,这些善后工作一般会在程序的顶层位置进行处理,如果是在一个内聚性比较强的功能模块中,则可以在模块的调用边界处放一个catch(...)捕捉所有的异常,进行统一的善后工作,同时防止模块内的异常泄露到模块之外。
最后一点,保证异常安全的强有力工具当然是RAII了,具有的用法可以查询相关资料获取。
最后
以上就是辛勤夕阳为你收集整理的C++错误处理方式的思考的全部内容,希望文章能够帮你解决C++错误处理方式的思考所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复