您的位置:学习中国 推荐教程 C++C语言 正文
原作者:kuku 添加时间:2007-06-02 原文发表:2007-06-02 人气:17 来源:互联网

本文章共5434字,分4页,当前第3页,快速翻页:
 
-----------------------------------------------------------------------------------------------------------------------------------------------

本文提示:《如何编写异常安全的C++代码(3)》是本站编辑们为广大网友精选的实用文章,本文阐述了关于文章的相关理论,相对来说专业性强,但是本文只是针对于某个问题提出的见解与论述,未必能辐射到相关问题的方方面面,所以本文处理问题的方法仅仅为您提供一些参考。更多问题请查阅学习中国网其他栏目哦.

-----------------------------------------------------------------------------------------------------------------------------------------------



  关于何时抛出异常的回答中,并不排斥返回值报告错误,而且这两者是正交的。然而,从我们经验上来说,完全可以在这两者中加以选择,这又是为什么呢?事实上,当我们做出这种选择时,必然意味着接口语意的改变,在不改变接口的情况下,其实是无法选择的(试试看,用返回值处理构造函数中的错误)。通过不变式区别出正常和异常状况,还可以更好地提炼接口。

  对于异常安全的评定,可分为三个级别:基本保证、强保证和不会失败。

  基本保证:确保出现异常时程序(对象)处于未知但有效的状态。所谓有效,即对象的不变式检查全部通过。

  强保证:确保操作的事务性,要么成功,程序处于目标状态,要么不发生改变。

  不会失败:对于大多数函数来说,这是很难保证的。对于C++程序,至少析构函数、释放函数和swap函数要确保不会失败,这是编写异常安全代码的基础。

  首先从异常情况下资源管理的问题开始.很多人可能都这么干过:

Type* obj = new Type;
try{ do_something...}
catch(...){ delete obj; throw;}

  不要这么做!这么做只会使你的代码看上去混乱,而且会降低效率,这也是一直以来异常名声不大好的原因之一. 请借助于RAII技术来完成这样的工作:

auto_ptrobj_ptr(new Type);
do_something...

  这样的代码简洁、安全而且无损于效率。当你不关心或是无法处理异常时,请不要试图捕获它。并非使用try...catch才能编写异常安全的代码,大部分异常安全的代码都不需要try...catch。我承认,现实世界并非总是如上述的例子那样简单,但是这个例子确实可以代表很多异常安全代码的做法。在这个例子中,boost::scoped_ptr是auto_ptr一个更适合的替代品。

  现在来考虑这样一个构造函数:

Type() : m_a(new TypeA), m_b(new TypeB){}

  假设成员变量m_a和m_b是原始的指针类型,并且和Type内的申明顺序一致。这样的代码是不安全的,它存在资源泄漏问题,构造函数的失败回滚机制无法应对这样的问题。如果new TypeB抛出异常,new TypeA返回的资源是得不到释放机会的.曾经,很多人用这样的方法避免异常:

Type() : m_a(NULL), m_b(NULL){
 auto_ptrtmp_a(new TypeA);
 auto_ptrtmp_b(new TypeB);
 m_a = tmp_a.release();
 m_b = tmp_b.release();
}

  当然,这样的方法确实是能够实现异常安全的代码的,而且其中实现思想将是非常重要的,在如何实现强保证的异常安全代码中会采用这种思想.然而这种做法不够彻底,至少析构函数还是要手动完成的。我们仍然可以借助RAII技术,把这件事做得更为彻底:shared_ptrm_a; shared_ptrm_b;这样,我们就可以轻而易举地写出异常安全的代码:

Type() : m_a(new TypeA), m_b(new TypeB){}

  如果你觉得shared_ptr的性能不能满足要求,可以编写一个接口类似scoped_ptr的智能指针类,在析构函数中释放资源即可。如果类设计成不可复制的,也可以直接用scoped_ptr。强烈建议不要把auto_ptr作为数据成员使用,scoped_ptr虽然名字不大好,但是至少很安全而且不会导致混乱。

  RAII技术并不仅仅用于上述例子中,所有必须成对出现的操作都可以通过这一技术完成而不必try...catch.下面的代码也是常见的:
a_lock.lock();

try{ ...} catch(...) {a_lock.unlock();throw;}
a_lock.unlock();

  可以这样解决,先提供一个成对操作的辅助类:

struct scoped_lock{
 explicit scoped_lock(Lock& lock) : m_l(lock){m_l.lock();}
 ~scoped_lock(){m_l.unlock();}
 private:
  Lock& m_l;
};

  然后,代码只需这样写:

scoped_lock guard(a_lock);
do_something...

  清晰而优雅!继续考察这个例子,假设我们并不需要成对操作, 显然,修改scoped_lock构造函数即可解决问题。然而,往往方法名称和参数也不是那么固定的,怎么办?可以借助这样一个辅助类:
 

本文章更多内容<<上一页 - 1 - 2 - 3 - 4 - 下一页>>
本页地址
相关文章

在c++程序中重启自己的一种方法
踏入C++中的雷区——C++内存管理详解
C++的救赎 C++开源程序库评话
初学者必备:C++经典入门详细教程
C++对象的拷贝与赋值操作
c语言中的指针专题
C++:最强大的.NET语言之对象构造
在CPP中调用Jscript中的函数
要求对象产生于Heap之中
C++中要求(或禁止)对象产生于heap中
C++程序员必需的修养
设计OutLook风格的工具栏
创建可移植的64位应用程序代码
C++箴言:为类型信息使用特征类
探索C++的秘密之详解extern C
C++箴言:用成员函数模板接受兼容类型
C++之父Bjarne谈C++中的STL模板
对象布局及多态探索之菱形结构虚继承
如何访问模板化基类中的名字
C++对象布局及多态之虚成员函数调用

相关评论


本文章所属分类:首页 推荐教程 C++C语言   C++C语言