【来源】StackOverflow的提问:“Conventions for exceptions or error codes”
明显,大部分回答者更偏向于使用exception,认为error_code方式要处理的东西太多。
一位被推崇的程序员的博客:“https://nedbatchelder.com/text/exceptions-vs-status.html”,这篇长文说明了exception更加优越的原因。
原因一:exception让代码更加的简洁(clean)
原因二:更多的错误信息
比起返回一个int型错误码,exception能给出更多的信息。
虽然错误码能使用error message来辅助,但是error message有的时候很难抉择需不需要实际执行。
举个例子,callee内部有一个打开头文件操作,如果文件打开失败,它会返回一个错误码,但是它要不要在日志输出error message,比较难抉择。
首先,假设它输出了,这时如果caller认为这其实并不是个错误,打不开就打不开,可能确实不存在某些文件。这时候打出来的error message 就是误报。
如果它没log出来,碰上另一个caller认为这是一个错误,那这就会导致漏报。
最终形成的一个模式就是:作为库函数的callee永远不敢输出error message,直到最外层的caller才敢报,但是最外层能报错的信息和实际报错的起源地址获取得到的信息是不一样的。
本例子中,callee知道打不开的文件名是什么并且能告诉用户,但是caller可能不知道,(文件名在callee里面生成),甚至caller都不知道callee还有打开文件的操作,所以caller无法直接的指出到底是什么原因导致fail,而只能不负责的打一句“callee fail!”,留下一头雾水的用户。
原因三:返回值被占用
很多时候,我们希望函数能够通过返回值的形式提供结果,这样更符合输入输出的阅读结构。 返回值被错误码占用之后,我们只能使用形参作为载体。显然返回值相比形参更能体现“这是一个输出”的情况。 C++11增加的tuple实现了“多个返回值”这个feature,这显然说明大家更喜欢在返回值上下功夫。
原因四:有些函数无法定义返回值
例如构造函数和析构函数。不用返回值,那么只能将绝对不会fail的部分放到构造函数,把其他部分使用一个显示的函数执行,例如initialization。
这本身也衍生了一个关于constructor和initialization的话题: 构造函数(constructor) vs 初始化函数(initialization) 。
这个话题的结果也很明显,大家更偏向于构造函数,比较契合OOP的精神。
也有人说出了exception不好的地方:
【来源】https://www.joelonsoftware.com/2003/10/13/13/
(这点似乎有点站不住脚)
(这一点貌似很有道理)
In an effort to reduce code and executable size, LLVM does not use RTTI (e.g. dynamic_cast<>;) or exceptions.