IT技术博客大学习 共学习 共进步

僵尸对象或 RAII

Solrex Shuffling 2011-05-25 13:41:04 浏览 3,424 次

    我最近在想这个问题,到底要不要在程序中使用异常?

    以前写的 C 代码比较多,即使写 C++,基本上也是把它当成 C with object 来用。对异常的了解偏少,使用更是极少。最近评审别人代码的时候遇到一个问题:如果构造函数中 new 失败了,会发生什么事情?

    工程的代码一般提倡哪里出错在哪里处理,不能恢复的要返回错误码给调用者。在一般情况下,使用 new(std::no_throw) 保证 new 不抛出异常(否则结果是灾难性的),并且检查分配是否成功是可以实现这一点的。

    遗憾的是构造函数没有返回值,我们不能返回构造失败。那么只有用迂回的办法,为类定义一个成员变量 bool inited。初始化为 false,只有在构造的工作都完成之后,才将它置为 true。如果一个对象的 inited 成员为 false,就意味着它构造过程中出了问题,不能被使用。这就是一个僵尸对象,“活死人”。

    看,我们成功地规避了使用异常。但是慢着,不是只有 bad_alloc 这一个异常啊!还有 bad_cast、runtime_error、logic_error,还有:

$ grep class /usr/include/c++/4.5/stdexcept
// Standard exception classes  -*- C++ -*-
// ISO C++ 19.1  Exception classes
   *  program runs (e.g., violations of class invariants).
   *  @brief One of two subclasses of exception.
  class logic_error : public exception
  class domain_error : public logic_error
  class invalid_argument : public logic_error
  class length_error : public logic_error
  class out_of_range : public logic_error
   *  @brief One of two subclasses of exception.
  class runtime_error : public exception
  class range_error : public runtime_error
  class overflow_error : public runtime_error
  class underflow_error : public runtime_error 

    天那,我未曾注意过标准库有那么多异常!那么如果在使用标准库时,不小心触发了什么异常,OMG!

    这样看来,使用异常是很有必要的。但是,麻烦的问题又来了,一旦使用异常,函数的退出过程就变了。使用错误码有一个好处,就是你可以在函数返回前擦干净自己的屁股;但是使用异常呢?你既要保证对象能够自己擦屁股(RAII),还要保证函数能自己擦屁股(在正确的位置使用异常处理),这样才能在 stack unwinding 时不会导致内存泄露。

    还有一个麻烦是,你要遵从约定――特别是对于一个程序库作者来说。如果约定出错时抛出异常,那么可以抛;如果约定出错时返回错误码,或者这个库可能被 C 调用,那么抛出异常就可能是灾难。

    现在看来,如果想实现更健壮的 C++ 程序,那么异常处理是不可或缺的。但在使用异常处理之前,必须得了解在哪里、怎样抛出和捕获异常,如果是团队合作,可能还需要有简单的操作指导手册,否则使用不当或者过量的异常也可能带来麻烦。

    我还在路上!

建议继续学习

  1. PHP的异常原理与实例说明 Fatal error: Uncaught exception (阅读 9,642)
  2. 是返回错误码,还是抛出异常?说说我的选择 (阅读 7,381)
  3. Perl 异常处理之 autodie 和 Try::Tiny (阅读 3,585)
  4. PHP错误处理及异常处理 (阅读 3,402)
  5. 深入理解PHP之异常机制 (阅读 3,381)
  6. 异常的代价 (阅读 3,383)
  7. PHP程序员也要学会使用“异常” (阅读 3,283)
  8. 我们什么时候应该使用异常? (阅读 3,123)
  9. Java处理InterruptedException异常小结 (阅读 2,922)
  10. Swift错误和异常处理 (阅读 1,785)