C++异常处理的是与非

以下观点是从各处收集加上自己的理解而来的,可能会有不少纰漏,欢迎指正。

公司提倡使用异常,但部门不提倡,到了项目里基本上是禁止的态度了,基于“局部变量”覆盖“全局变量”的原则,我没怎么在项目里使用异常。所以对异常的使用的弊端暂时还体会不多,但对不使用异常的不方便体会倒是不少,所以以下观点可能更偏向于异常的优点。

关于C++的异常处理,众说纷纭,而且经常出现有部分人强烈支持的、有部分人强烈反对,总结一下,异常的优点与缺点如下:

优点:

异常不能不加修改地忽略

相较于错误码的处理方式,使用异常的好处是除非显式指明忽略,它会中止程序的进一步运行。

比如,C++的new在内存不足时默认的处理方式就是抛出异常,如果用户没显式的处理该异常,程序就会直接core掉,让用户很清楚的能从core文件中分析出程序挂在哪儿了。

而malloc则不然,在用户没有显示处理时,默认是情况下会忽略掉该错误,这样,就很有可能在程序错误运行了半个小时之后,当真正用到这个malloc得到的指针时才core掉。

而这个情况其实还算好的,至少程序还会在你用这个指针时core掉,而有的时候,没准就因为一个返回值没检查,你已经错误的运行了很久以后才知道之前早就出错了,这时候程序的栈早就乱得不知道是啥样儿了,而且错误运行那么久,没准已经错误的把许多不该修改的外部存储状态都修改得乱七八糟了。

异常处理能使正常代码与错误处理的代码分离,使代码清晰

这句话成立的前提是:不要使用异常来进行正常的跳转。

如果你正确的使用异常,就可以只在catch中进行错误的处理、恢复,使代码的逻辑更清晰。

但如果你偏偏要用异常来做正常的跳转,那就会起来完全相反的效果,那错误处理、恢复流程与正确流程将比错误码的方式更难区分。

构造函数只能使用异常来报告错误

如果禁用异常,当构造失败时,常见的办法是通过一个init方法来初始化对象,然后通过判断init的返回值判断构造是否成功。

然后,我们会发现,当这个对象析构的时候,我们又需要通过一些方法确定哪部分析构操作该执行,哪部分操作不该执行。此处就很容易出现问题,经常会过多的执行一些不该执行的析构操作。

而在构造函数中抛出异常则不会调用该对象的析构函数,用户只需要在构造函数中恢复自己对外部造成的恶劣影响即可。

许多操作符重载只能使用异常来报告错误

操作符重载在C++中也是处于一个比较尴尬的地位,但相对于异常处理的众说纷纭,操作符重载还是相对容易达成共识的:

只有当重载运算符对该类很自然时才重载,绝不贪图使用方便而盲目重载运算符。

而许多时候,为了使操作符重载更为自然,就需要将操作符的使用模拟得与原生的操作符基本一致。

这时,就导致一个问题,如果操作符重载时,出现异常该如何返回?总不能为了一个加法操作符而在返回值里加一个标志位吧?

基本上只有异常能拯救你。

缺点:

异常处理机制性能较差

关于这点,在百度的编码规范中提到测试表明,一个单线程程序每秒能抛出、接住25万条异常,比打一条日志还快。(这里指的异常还是指的经过公司封装之后的一个异常类,其中添加了一些自定义的用于分析错误的信息)

只有在你的程序抛出异常太多的情况下才会真正影响到性能,这个担心可能是不必要的,只需要你记住前面已经提过的一句话:“不要使用异常来进行正常的跳转”。你的程序中就不可能出现那么多异常。如果即使这样,你的程序每秒还是抛出几万条异常,那你还是查查你的程序是不是有啥严重的问题吧。

在一些情况下导致代码可读性变差,或者导致代码重复

参见zeromq作者的一篇文章:

http://www.250bpm.com/blog:4

实际上我认为martin sustrik举的例子恰好是用异常来解决正常的逻辑,他提到的许多时候,异常抛出到上层之后,我们需要在不同的调用处用同一段逻辑来解决这个异常,导致代码的重复。其实这种用法的根本原因在于,在不恰当的场景下使用了异常。一般只有出现错误,而本函数内不能确定该如何处理该异常,才会将其抛出;当异常抛出时可以清楚的确定这个错误该如何恢复时,这个错误恢复的过程应该在函数内部完成,而非抛出异常。

许多时候写出异常安全的代码是相当困难的

比如以下代码:

如果不使用异常,它就是很安全的,可foo里抛出了异常就很有可能导致file没有关闭。

如果想避免这种问题,就需要使用RAII机制,关于RAII请参考我的另一篇文章: :ref:`浅谈C++RAII<cpp-raii-top>`。

Google在编码规范中提到:

异常安全要求同时采用 RAII 和不同编程实践. 要想轻松编写正确的异常安全代码, 需要大量的支撑机制配合. 另外, 要避免代码读者去理解整个调用结构图, 异常安全代码必须把写持久化状态的逻辑部分隔离到 “提交” 阶段。它在带来好处的同时, 还有成本 (也许你不得不为了隔离 “提交” 而整出令人费解的代码). 允许使用异常会驱使我们不断为此付出代价, 即使我们觉得这很不划算。

不过Google到最后还是说,表面看来使用异常带来的好处是比坏处要大的多的。但是,由于公司里一堆堆旧的,异常不安全的项目,为了兼容老的异常不安全的项目,新的项目也尽量别使用异常了。

(参见http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions)

也就是说最终google是因为项目管理的原因而禁止使用异常的。

返回顶部


转载请指明出处:http://blog.acmol.com