谈C 继承中的重载、覆盖和隐藏
2008-02-23 05:32:58来源:互联网 阅读 ()
使用异常还是返回错误码?这是个争论不休的话题。大家一定听说过这样的说法:只有在真正异常的时候,才使用异常。那什么是“真正异常的时候”?在回答这个问题以前,让我们先看一看程式设计中的不变式原理。
对象就是属性聚合加方法,如何判定一个对象的属性聚合是不是处于逻辑上正确的状态呢?这能够通过一系列的断言,最后下一个结论说:这个对象的属性聚合逻辑上是正确的或是有问题的。这些断言就是衡量对象属性聚合对错的不变式。
我们通常在函数调用中,实施不变式的检查。不变式分为三类:前条件,后条件和不变式。前条件是指在函数调用之前,必须满足的逻辑条件,后条件是函数调用后必须满足的逻辑条件,不变式则是整个函数执行中都必须满足的条件。在我们的讨论中,不变式既是前条件又是后条件。前条件是必须满足的,假如不满足,那就是程式逻辑错误,后条件则不一定。现在,我们能够用不变式来严格定义异常状况了:满足前条件,但是无法满足后条件,即为异常状况。当且仅当发生异常状况时,才抛出异常。
关于何时抛出异常的回答中,并不排斥返回值报告错误,而且这两者是正交的。然而,从我们经验上来说,完万能够在这两者中加以选择,这又是为什么呢?事实上,当我们做出这种选择时,必然意味着接口语意的改变,在不改变接口的情况下,其实是无法选择的(试试看,用返回值处理构造函数中的错误)。通过不变式区别出正常和异常状况,还能够更好地提炼接口。
对于异常安全的评定,可分为三个级别:基本确保、强确保和不会失败。
基本确保:确保出现异常时程式(对象)处于未知但有效的状态。所谓有效,即对象的不变式检查全部通过。
强确保:确保操作的事务性,要么成功,程式处于目标状态,要么不发生改变。
不会失败:对于大多数函数来说,这是很难确保的。对于C 程式,至少析构函数、释放函数和swap函数要确保不会失败,这是编写异常安全代码的基础。
首先从异常情况下资源管理的问题开始.很多人可能都这么干过:
Type* obj = new Type;
try{ do_something...}
catch(...){ delete obj; throw;}
不要这么做!这么做只会使您的代码看上去混乱,而且会降低效率,这也是一直以来异常名声不大好的原因之一. 请借助于RAII技术来完成这样的工作:
auto_ptr
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_ptr
auto_ptr
m_a = tmp_a.release();
m_b = tmp_b.release();
}
当然,这样的方法确实是能够实现异常安全的代码的,而且其中实现思想将是很重要的,在如何实现强确保的异常安全代码中会采用这种思想.然而这种做法不够完全,至少析构函数还是要手动完成的。我们仍然能够借助RAII技术,把这件事做得更为完全:shared_ptr
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构造函数即可解决问题。然而,往往方法名称和参数也不是那么固定的,怎么办?能够借助这样一个辅助类:
template
struct pair_guard{
pair_guard(FEnd fe, FBegin fb) : m_fe(fe) {if (fb) fb();}
~pair_guard(){m_fe();}
private:
FEnd m_fe;
...//禁止复制
};
typedef pair_guard
好了,借助boost库,我们能够这样来编写代码了:
simple_pair_guard guard(bind(&Lock::unlock, a_lock), bind(&Lock::lock, a_lock) );
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash