关于c++:More-Effective-C总结笔记二异常

42次阅读

共计 3271 个字符,预计需要花费 9 分钟才能阅读完成。

异样

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

正文完
 0