• 网络学院
  • IT资讯
  • 操作系统
  • 网络技术
  • 软件应用
  • 办公软件
  • 编程技术
  • 网站架设
  • 数据库类
  • 平面设计
  • 多媒体类
  • 游戏资讯
  • 教学论文
  • 认证考试
C++箴言:防止异常离开析构函数
  站点:
  • 首 页
  • 最新软件
  • 文章教程
  • 国内软件
  • 国外软件
  • 绿色软件
  • 源码下载
  • 字体下载
C++箴言:防止异常离开析构函数
软件发布 C++箴言:防止异常离开析构函数
网络软件 系统工具 应用软件 联络聊天 图形图像 多媒体类 行业软件 游戏娱乐 编程开发 安全相关 教育教学 数码软件 绿软下载
热门软件: QQ 瑞星 pplive e话通 木马克星 千千静听 office2000 五笔字根 Photoshop 视频分割
返回文章教程首页 >> 文章首页 >> 编程技术 >> C/C++教程 >> C++箴言:防止异常离开析构函数

C++箴言:防止异常离开析构函数

添加时间: 2006-2-12 9:29:14  作者: C++教程  阅读次数:108   来源: http://www.d9soft.com

       

  C++ 并不禁止从析构函数中引发异常,但是这确实妨碍了实践。至于有什么好的理由,考虑:

  

  class Widget {

   public:

    ...

    ~Widget() { ... } // assume this might emit an exception

  };

  

  void doSomething()

  {

   std::vector v;

   ...

  } // v is automatically destroyed here

  当 vector v 被析构时,它有责任销毁它包含的所有 Widgets。假设 v 中有十个 Widgets,在销毁第一个的时候,抛出一个异常。其他 9个 Widgets 仍然必须被销毁(否则他们持有的任何资源将被泄漏),所以 v 应该调用它们的析构函数。但是假设在这个调用期间,第二个 Widgets 的析构函数又抛出一个异常。现在有两个异常同时在活动中,对于 C++ 来说这太多了。在非常巧合的条件下发生这样两个同时活动的异常,程序的执行会终止或者引发未定义行为。在本例中,将引发未定义行为。与此相同,使用任何标准库容器(比如,list,set),任何 TR1中的容器,甚至是一个数组,都可能会引发未定义问题。并非必须是容器或数组才会陷入麻烦。程序夭折或未定义行为是析构函数引发异常的结果,即使没有使用容器或数组也会如此。C++ 不喜欢引发异常的析构函数。 这比较容易理解,但是如果你的析构函数需要执行一个可能失败而抛出异常的操作,该怎么办呢?例如,假设你与一个数据库连接类一起工作:

  

  class DBConnection {

   public:

    ...

   

    static DBConnection create(); // function to return

    // DBConnection objects; params

    // omitted for simplicity

  void close(); // close connection; throw an

  }; // exception if closing fails

  为了确保客户不会忘记调用 DBconnection 对象的 close,一个合理的主意是为 DBConnection 建立一个资源管理类,在它的析构函数中调用 close。这样的资源管理类将在以后的文章中探讨,但在这里,只要认为这样一个类的析构函数看起来像这样就足够了:

  

  class DBConn { // class to manage DBConnection

   public: // objects

    ...

    ~DBConn() // make sure database connections

    { // are always closed

     db.close();

    }

   private:

    DBConnection db;

  };

  它允许客户像这样编程:

  

  {

   // open a block

   DBConn dbc(DBConnection::create()); // create DBConnection object

   // and turn it over to a DBConn

   // object to manage

   ... // use the DBConnection object

   // via the DBConn interface

  } // at end of block, the DBConn

  // object is destroyed, thus

  // automatically calling close on

  // the DBConnection object

  既然能成功地调用 close 那就好了,但是如果这个调用导致了异常,DBConn 的析构函数将散播那个异常,也就是说,它将离开析构函数。这就产生了问题,因为析构函数抛出了一个烫手的山芋。
  有两个主要的方法避免这个麻烦。DBConn 的析构函数能:

  终止程序 如果 close 抛出异常,调用 abort。

  

  DBConn::~DBConn()

  {

   try { db.close(); }

   catch (...) {

    make log entry that the call to close failed;

    std::abort();

   }

  }

  如果程序在析构过程遭遇到错误后不能继续运行,这就是一个合理的选择。它有一个好处是:如果允许从析构函数散播异常可能会引起未定义行为,这样就能防止它发生。也就是说,调用 abort 就预先防止了未定义行为。

  抑制这个异常 起因于调用 close:

  

  DBConn::~DBConn()

  {

   try { db.close(); }

   catch (...) {

    make log entry that the call to close failed;

   }

  }

  通常,抑制异常是一个不好的主意,因为它会隐瞒重要的信息——某些事情失败了!可是,有些时候,抑制异常比冒程序夭折或未定义行为的风险更可取。程序必须能够在遭遇到错误并忽略之后还能继续可靠地执行,这才能成为一个可行的选择。

  这些方法都不太吸引人。它们的问题在于程序无法在第一现场对引起 close 抛出异常的条件做出回应。

  一个更好的策略是设计 DBConn 的接口,以使它的客户有机会对可能会发生的问题做出回应。例如,DBConn 能够自己提供一个 close 函数,从而给客户一个机会去处理从那个操作中发出的异常。它还能保持对它的 DBConnection 是否已被关闭的跟踪,如果没有关闭就在析构函数中自己关闭它。这样可以防止连接被泄漏。如果在 DBConnection 的析构函数中调用 close 失败,无论如何,我们还可以再返回到终止或者抑制。

  

  class DBConn {

  public:

  ...

  

  void close() // new function for

  {

   // client use

   db.close();

   closed = true;

  }

  

  ~DBConn()

  {

   if (!closed) {

    try { // close the connection

     db.close(); // if the client didn’t

    }

    catch (...) { // if closing fails,

     make log entry that call to close failed; // note that and

     ... // terminate or swallow

    }

   }

  

   private:

    DBConnection db;

    bool closed;

  };

  将调用 close 的责任从 DBConn 的析构函数转移到 DBConn 的客户(同时在 DBConn 的析构函数中包含一个“候补”调用)可能会作为一种肆无忌惮地推卸责任的做法而刺激你。你甚至可以把它看作一个忠告(使接口易于正确使用)的违背。实际上,这都不正确。如果一个操作可能失败而抛出一个异常,而且可能是一个需要处理的异常,这个异常就必须来自非析构函数。这是因为析构函数引发异常是危险的,永远都要冒着程序夭折或未定义行为的风险。在此例中,让客户调用 close 并不是强加给他们的负担,而是给他们一个时机去应付错误,否则他们将没有机会做出回应。如果他们找不到可用到机会(或许因为他们相信不会有错误真的发生),他们可能忽略它,依靠 DBConn 的析构函数为他们调用 close。如果一个错误恰恰发生在那时——如果由 close 抛出——如果 DBConn 抑制了那个异常或者终止了程序,他们将无处诉苦。毕竟,他们无处着手处理问题,他们将不再使用它。

  Things to Remember

  ·析构函数应该永不引发异常。如果析构函数调用了可能抛出异常的函数,析构函数应该捕捉任何异常,然后抑制它们或者终止程序。

  ·如果类客户需要能对一个操作抛出的异常做出回应,则那个类应该提供一个常规的(非析构函数)函数来完成这个操作。

 

上下文章:

 

上一篇文章: C++箴言:让=返回一个*this的引用 下一篇文章: C#编程实现Excel文档中搜索文本

相关文章:

  • Qmail系统下防止滥用mail relay方案(二)
  • 防止垃圾邮件中转的总结-两个虚拟SMTP服务器
  • OracleSQL内置函数大全之三
  • Oracle常用的OCI函数上
  • OracleSQL内置函数大全之二

相关软件:

  • 函数图像作图器 1.0
  • DLL导出函数查看器 V1.3
  • VB函数添加大师 V2.23
  • Excel2003函数应用完全手册(PDF)
  • VB API函数参考手册(CHM)
  • Java计算机语言函数应用(PDG)

 

快速导航

  • 网络学院
  • 精品汇聚
  • 字体下载
  • 教程下载
  • ASP源码
  • PHP源码
  • Net源码
  • JSP 源码

编程技术分类导航

  • ASP & ASP.NET教程
  • PHP教程
  • JSP教程
  • C/C++教程
  • VB & VB.NET教程
  • VC教程
  • Delphi教程
  • BCB教程
  • VFP教程
  • PB教程
  • JAVA教程
  • XML教程
  • C#教程
  • CGI教程

本类经典文章推荐

  • C/C++作用域引申出的编码规范
  • 程序宝典:C++学习感想
  • C语言:黑客学员必修课(一)
  • C语言:黑客学员必修课(二)
  • 使用多线程实现数据实时采集
  • C++编程中的四个调试小技巧
  • C语言程序设计基础之联合
  • 学C++不得不看的一篇文章
  • C,曾经的程序员梦
  • 水滴石穿C语言之可变参数问题

C/C++教程阅读排行

  • 学C++不得不看的一篇文章
  • 水滴石穿C语言之可变参数问题
  • C语言:黑客学员必修课(一)
  • 程序宝典:C++学习感想
  • 我的Visual C++入门之路
  • 初学者学习C++的50条忠告
  • C++中的struct专题研究
  • C++箴言:只要可能就用const
  • 强大的语言——C入门
  • C++编程中的四个调试小技巧

编程技术阅读总排行

  • VB入门教程之一
  • Java连接数据库实例
  • 第二章 PowerBuilder 入门之创建新...
  • VC++之List Box/Check List Box控...
  • 第一章 什么是PowerBuilder
  • VC++ List Ctrl控件
  • VC++ Combo Box/Combo Box Ex控件
  • 学C++不得不看的一篇文章
  • VB入门教程之二
  • VC++之Button控件

广告位置

字母检索 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 回到顶部

关于我们 | 版权声明 | 免责条款 | 广告联系 | 软件发布 | 下载帮助 | 下载排行 | 网站地图 | 特别鸣谢 | 友情连接

copyright; 2005-2008 D9soft.com 第九软件网 版权所有