#Exception#Cpp引入异常的原因、关于异常的吐槽以及何时使用异常

在知乎上看到关于“Cpp的异常”的讨论,在此整理一下。

 


C++ 引入异常的原因:

1、为了能让构造函数报错(析构函数不能抛异常这是大家都知道的常识),毕竟构造函数没有返回值,没有异常的话调用方如何得知对象构造是否成功呢?

2、让 dynamic_cast<Derived&>(baseReference) 能报错,因为没有 null reference。

3、让 overloaded operator 能报错,毕竟 operator 的返回类型往往无法包含 error code,例如 operator=() 返回的是 Type&。C++ 也是唯一一个变量赋值有可能会抛异常的语言,例如 Person s; s = getPersonById(someId);,那么即便 getPersonById() 不抛异常也不能保证上一句赋值不抛异常。)

(说白了,就是为了给那些无法提供额外信息的函数提供额外信息的机会)
4、政治原因。 Ada 支持异常,而 Ada 是 DoD 的指定官方语言,如果 C++ 不支持异常,那么 AT&T 贝尔实验室就不能拿 C++ 做 DoD 的项目。)


 

C++异常机制被吐槽的方面

整个 C++ exception 的行为在常见语言中是最奇葩的, 因为这个语言特性与 C++ 其他 feature(特别是确定性析构) 格格不入。在 C++ 中全面铺开使用异常会遇到其他语言中不存在的问题。编译器/标准库为了让构造函数能抛异常却是麻烦重重:

  1. 数组元素构造时抛异常,前面已经构造好的元素要析构,还没有构造的元素不能析构。
  2. 构造函数的初始化列表里抛异常,前面已经构造好的成员和基类子对象要析构,还没有构造的成员则不能析构。而且这个异常捕获之后必须重新抛出(编译器强制),因为C++不允许“半吊子”构造的对象存在。
  3. 多继承中某个基类的构造函数抛异常,那么已经构造好的基类子对象要析构,还没有构造的基类子对象则不能析构。虚拟继承,虚基类只能析构一次,你慢慢想吧。
  4. 函数实参对象构造时抛异常,那么多个实参中已经构造好的实参对象要析构,尚未构造的实参对象不能析构。
  5. std::vector 在 resizing 的时候某个元素的拷贝发生异常,那么前面已经拷贝的元素要析构,尚未拷贝的元素则不必也不能析构,去看 gcc vector::_M_insert_aux 的代码有多麻烦。

C++ 编译器要随时提防调用某个函数 foo 会抛异常,这会阻止一些优化,也会产生很多累赘的代码(随时准备析构那些调用 foo 函数前已经构造好的栈上对象)。因此 C++11 的 noexcept 应该大力推广。 

 

 

什么时候使用异常?

1、一个项目中多个类都可能发生的共有的异常

2、处理意外错误状态,

比如打开文件,发现打开失败,这叫算意外

什么时候不使用异常?

1、简单,意料中的情况

2、只发生在单独函数中的简单错误

总结下来,不要将异常滥用于简单的逻辑判断

posted on 2015-07-30 21:56  依风152  阅读(470)  评论(0编辑  收藏  举报