异常
条款9:利用destructors避免泄露资源
- 只要坚持这个规则,把资源封装在对象内(相似智能指针shared_ptr),一般即可以在exceptions出现时避免泄露资源。
- 简单来讲就是,当有资源可能在函数抛异常时而没法释放,这时能够将资源封装到对象内(RAII),利用对象的析构函数来自动释放资源,这样即便有exceptions发生,也不会有资源泄露。
条款10:在constructors内阻止资源泄露(resource leak)
- C++只会析构已构造完成的对象。对象只有在其constructor执行完毕才算是完成构造稳当。
- 因为C++不自动清理那些“构造期间抛出exceptions”的对象,因此你必须设计你的constructors,使它们在那种状况下亦能自我清理。一般这只须要将全部可能的exceptions捕捉起来,执行某种清理工做,而后从新抛出exception,使它继续传播出去便可。
- 可是,更优雅的作法其实是将这些须要在constructor内初始化的对象视为资源,将它们交由智能指针来管理。
- 结论是:若是你以auto_ptr(C++11后使用shared_ptr或者unique_ptr)对象来取代pointer class members,你便对你的constructors作了强化工事,免除了“exceptions出现时发生资源泄露”的危机,再也不须要在destructors内亲自动手释放资源,并容许const member pointers得以和non-const member pointers有着同样优雅的处理方式。
条款11:禁止异常(exceptions)流出destructors以外
Session::Session()
{
try {
logDestruction(this);
}
catch (...) {
}
}
- 这里的catch语句块看起来什么都没作,可是外表容易骗人。这个语句块阻止了“logDestruction所抛出的exceptions”传出Session destructor以外。
- 有两个好理由支持咱们“全力阻止exceptions传出destructors以外”。第一,它能够避免terminate函数在exception传播过程的栈展开(stack-unwinding)机制中被调用;第二,它能够协助确保destructors完成其应该完成的全部事情。
条款12:了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差别
- “抛出exception”与“传递参数”的相同点是:它们的传递方式有3中:by value(传值),by reference(传引用),by pointer(传指针)。然而视你所传递的是参数或exceptions,发生的事情可能彻底不一样。缘由是当你调用一个函数,控制权最终会回到调用端(除非函数失败以致于没法返回),可是当你抛出一个exception,控制权不会再回到抛出端。
- “抛出exception”与“传递参数”的区别一:C++特别声明,一个对象被抛出做为exception时,老是会发生复制(copy)。即便catch语句参数时by reference,或者抛出对象声明为static也同样会发生复制。且复制动做永远是以对象的静态类型为本。
- “exception objects一定会形成复制行为”这一事实也解释了“传递参数”和“抛出exception”之间的另外一个不一样:后者经常比前者慢。
- 通常而言,你必须使用一下语句:
throw;
才能从新抛出当前的exception,其间没有机会让你改变被传播的exception的类型。此外,它也比较有效率,由于不须要产生新的exception object。架构
- “抛出exception”与“传递参数”的区别二:函数调用过程当中将一个临时对象传递给一个non-const reference参数时不容许的,但对exception则属合法。
- “抛出exception”与“传递参数”的区别三:通常而言,调用函数传递参数过程当中容许的隐式转换在“exceptions与catch子句相匹配”的过程当中使不会发生的。
- “exceptions与catch子句相匹配”的过程当中,仅有两种转换能够发生。第一种是“继承架构中的类转换”。第二种是从一个“有型指针”转为“无型指针”(因此一个参数为const void*的catch子句,可捕捉任何指针类型的exception)。
- “抛出exception”与“传递参数”的区别四:catch子句老是依出现顺序作匹配尝试。与虚函数调用比较,虚函数采用”best fit“(最佳吻合)策略,而exception处理机制采用”first fit“(最早吻合)策略。所以,绝对不要将”针对base class而设计的catch子句“放在”针对derived class而设计的catch子句“以前。
条款13:以by reference方式捕捉exceptions
- 相比于by reference方式,以by value方式捕获exception,会使被传递的对象发生两次复制,产生两个副本。其中一个构造动做用于“任何exceptions都会产生的临时对象”身上,另外一个构造动做用于“将临时对象复制到catch的参数上”。
- 千万不要抛出一个指向局部对象的指针,由于该局部对象会在exception传离其scope时被销毁,所以catch子句会得到一个指向“已被销毁的对象”的指针。
- 若是catch by reference,你就能够避开对象删除问题(by pointer会面对的)——它会让你动辄得咎,作也不是,不作也不是;你也能够避开exception objects的切割问题(派生类对象的exception objects被捕捉并被视为基类对象的exception objects,将失去其派生成分。对象切割问题是因为静态编联致使的,使用引用和指针时不会发生);你能够保留捕捉C++标准exceptions的能力;你也约束了exception objects需被复制的次数。
条款14:明智运用exception specifications
- exception specification示例,一个只抛出int类型exceptions的函数声明为:
void fun() throw(int);
- 若是函数抛出一个并未列入exception specification的exception,这个错误会在运行时期被检验出来,因而特殊函数unexpected会被自动调用。unexpected的默认行为是调用terminate。
想要避免这种unexpected的方法就是:函数
- 不该该将templates和exception specifications混合使用。
- 若是A函数内调用了B函数,而B函数无exception specifications,那么A函数自己也不要设定exception specifications。
- 处理“系统”可能抛出的exceptions(如bad_alloc)。
- C++容许你以不一样类型的exceptions取代非预期的exceptions。若是非预期函数的替代者从新抛出当前的exception,该exception会被标准类型bad_exception取而代之。
void convertUnexpected()
{
throw;
}
set_unexpected(convertUnexpected);
- 若是你作了上述安排,而且每个exception specifications都含有bad_exception,或者其基类,你就不再必担忧程序会因而非预期的exception时停止执行。
了解异常处理(exception handling)的成本
- 为了让exception的相关成本最小化,只要可以不支持exceptions,编译器便不支持;请将你对try语句块和exception specifications的使用限制于非用不可的地点,而且在真正异常的状况下才抛出exceptions。