C++ exception 七宗罪 (exception in c++ (2))

来源:互联网 发布:淘宝买东西怎么付款图 编辑:程序博客网 时间:2024/05/16 06:25

1. 晦涩的多重出口

一个函数最好只有一个出口。但是有时为了减少函数代码的复杂度,也会在代码中间,比如一个判断之后使用return跳出函数。

但是throw却和return不同。你无法的很快的判断throw之后的代码位置,你需要结合上下文,甚至是其他的函数来理解这一流程。你无法总是明确的知道一个throw会在什么时候被捕获。这是比goto更糟糕的东西!

虽然在happy path上你无需关注throw,但是却大大增加了调试的难度。而程序开发的大部分时间,都是在debugging而非coding。

2. 展开的堆栈有时无法跟踪

因为exception处理的存在,调试器中断后却不是停在原始的问题代码的地方,而是在异常处理的代码中。这种情况常常出现在异常处理不能有效的处理该异常的时候。此时堆栈已经经过多次展开,无法回到问题代码处,导致调试的成本大大增加,甚至错误的判断并把责任推给别人。

3. 不存在简单的异常处理代码

试图恢复一个异常,不如让他简单的crash。

对大部分异常的恢复都是徒劳的,只不过是把原本应该你处理的东西丢给别人处理而已。如果仅仅只是想在函数的结尾做集中处理,不如使用goto,简单而且高效。而如果想真真处理异常,代码量是比较大的。

虽然有的情况下这也是必须要做的事。这是我觉得异常还有一点用的地方。

4. 降低了程序的运行效率

不管spec.上是怎么定义的,编译器在实现的时候都插入了不少代码来实现异常机制。而且,编译器比如vs2005对异常代码是没有优化的,这让情况变得更加糟糕。

至于进入ill path,光堆栈展开的操作就可能耗费很多时间,因为所有的局部对象都需要析构。而此时,如果代码没有考虑到这一点,那么死锁或是其他更严重的问题也同时可能存在。 也就是说,在糟糕的代码中添加异常处理代码,很大可能会引入更多的问题,而不是

5. 增加设计难度

异常并非是out of the box的东西。

真真要在程序中使用异常,需要经过周密的设计。仅仅try catch那是还没有入门的学徒。

设计者要知道什么时候需要throw一个异常,throw一个什么类型的异常,这样的异常通常需要怎样的处理。调用者除了需要知道一个函数需要什么样的参数,返回什么值之外,还需要知道它会抛出什么样的异常,是否都需要截获?而此时,抛出的往往不是一个简单异常,而是一个异常类,i.e.,他们的复杂度是不一样的。即使是经过这样的设计,异常也并非全然有效。程序还是会在某个不确定的时候crash,因为某个异常没有被捕获。 而此时再想会过去调试,就需要很大的技巧和很多的知识了。

6. 给你稳定的假象

异常带来了调试上的问题,而更大的问题是:你以为程序是在正常的运行,其实它们却是在不停的抛出和处理异常。当你最终认识到这一点的时候,最佳的错误处理时机已经一去不复返了。这也是我之前所说的”不如让它crash“的原因。

7. 打破了代码设计的一些原则

C/C++中,{}定义了一个作用域。而try {} catch{}把一段代码分成了两个部分,try中的变量如果要在catch中进行判断,必须扩大它的作用域,此其一。

如果try中有多个条件或是循环,则条件或循环中的局部变量需要提出try,重名的需要改名,此其二。

throw增加了静态分析的难度,甚至令其变得不可能,i.e.破坏了代码的可读性,此其三。

虽然上文提及了异常的很多负面效果,但主旨并非是说不要使用异常处理,而是希望“不要随意使用异常处理”,更不可用异常处理来替换错误处理(见另一篇)。

 

(CSDN的blog还是没法完全兼容opera,实在是可惜了。)