异样
条款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。