关于C++异常处理的一些总结

来源:互联网 发布:动态ppt制作软件 编辑:程序博客网 时间:2024/06/08 23:56

以下是根据刘未鹏的错误处理(Error-Handling):为何、何时、如何(rev#2)整理而来


动机:为什么要进行错误处理

错误发生时,能恢复的要恢复。不能恢复的话,要保存用户数据,释放资源。可以得话需要记录日志、错误报告,重启程序。



什么是错误?
配置文件语法错误,文件由于访问权限无法打开,读写。网络连接失败,断开连接。数据库连接失败。
一个函数得前置条件无法得到满足,或者后置条件无法满足,无法维持其中的循环不变量(invariants)


什么不是错误?
1) 用于表示状态的结果不能算做错误,只是程序逻辑分支的一部分,它是一种状态码(status-code)。
如  if(foo(...)) {...}
   else {...}

   while(foo(...)) {...}

2)有一些“可恢复错误”,并不能算作真正的错误。
它一旦发生会转入一个错误恢复的子过程,后者尽量恢复主干程序所需要的前置条件。如果不能恢复的话,就变成了一个实在的错误
例如:读取一个配置文件失败,会去创建一个默认的配置文件。

如果无法恢复成功,也会转入第二个分支,虽然结果不能“完美执行”。例如内存申请失败,给出的错误提示。

3)因为代码bug导致无法满足某个invariants,不是错误。
 
经常出现的不是错误,它可能是一个分支逻辑,或是存在bug。

异常(exception) vs 错误码(error-code)
使用error-code 的问题
异常是跨越调用栈的,error-code需要一层层传播
1 麻烦,需要不断的向上层栈传播
2 不优雅且不可伸缩,代码嵌套过多,重复代码
使用异常
void foo()
{
try{
op1;
op2;
...
} catch (...){
//log
//clean up
throw;
}
}


使用error-code
int foo()
{
if(!op1()) goto FAILED;
if(!op2()) goto FAILED;
return SUCCEEDED;
FAILED:
//log, clean up
return FAILED;
}
整个函数保持中立的时候,异常的优势就显现了
3 忘了检查错误代码的后果


4 中间拿到错误码的时候需要检查,产生耦合

错误码占用了函数的返回值,异常是一个单独信道

异常可以携带丰富的信息,而错误吗只能使用GetLastError的方法

异常时OO的,可以任意层次上捕捉类型(IO异常,文件异常,SQL异常)

异常是强类型的,保证编译时检查

异常是first-class的语言机制


选择异常,误用也会带来问题


如何进行错误处理
1 何时抛出异常
2 何时捕获异常
3 如何保持异常中立


可恢复错误
1)在错误发生点上立刻恢复。如果不能恢复,向上抛一场
2) 在某个上层栈上恢复,抛给上层


不可恢复错误
1)sudden death 在错误发生点上退出模块。退出前释放资源,保存数据,记录日志
2) 回滚
回滚的两种方法  丢弃和撤销


异常转换
1 抛出一个抽象层的异常
2 对于C API,把异常转换为错误码

0 0