分享
 
 
 

C与C++中的异常处理15

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

1. 模板安全(续)

在异常安全的第二部分,我讲了在构造函数和析构函数中导致资源泄漏的问题。这次将探索另外两个问题。并且以推荐读物列表结束。

1.1 Problem #2:get

上次,我定义X::get()为:

T get()

{

return *value_;

}

这个定义有点小小的不足。既然get()不改变wrapper对象,我应该将它申明为const成员的:

T get() const

{

return *value_;

}

get()返回了一个T的临时对象。这个临时对象通过T的拷贝构造函数根据*value_隐式生成的,而这个构造函数可能抛异常。要避开这点,我们应该将get()修改为不返回任何东西:

void get(T &value) const throw()

{

value = *value_;

}

现在,get()接受一个事先构造好的T对象的引用,并通过引用“返回”结果。因为get()现在不调用T的构造函数了,它是异常安全的了。

真的吗?

很不幸,答案是“no”。我们只是将一个问题换成了另外一个问题而已,因为语句

value = *value_;

实际上是

value.operator=(*value_);

而它可能抛异常。更完备的解决方法是

void get(T &value) const throw()

{

try

{

value = *value_;

}

catch (...)

{

}

}

现在,get()不会将异常漏出去了。

不过,工作还没完成。在operator=给value赋值时抛异常的话,value将处于不确定状态。get()想要有最大程度的健壮接口的话,它必须两者有其一:

l value根据*value_进行了完全设置,或

l value没有被改变。

这两条要将我们弄跳起来了:无论我们用什么方法来解决这个问题,我们都必须调用operator=来设置value,而如果operator=抛了异常,value将只被部分改变。

我们的这个强壮接口看起来美却不实在。我们无法简单地实现它,只能提供一个弱些的承诺了:

l value根据*value_进行了完全设置,或

l value处于一个不确定的(错误)状态。

但还有一个问题没解决:让调用者知道回传的value是否是“好的”。一个可能的解决方法(也很讽刺的)是抛出一个异常。另外一个可能方法,也是我在这儿采用的方法是返回一个错误码。

修改后的get()是:

bool get(T &value) const throw()

{

bool error(false);

try

{

value = *value_;

}

catch (...)

{

error = true;

}

return error;

}

提供了一个较弱的承诺的这个新接口是安全的。它行为安全吗?是的。wrapper所拥有的唯一资源是分配给*value_的内存,而它是受保护的,即使operator=抛了异常。

符合最初的说明,get()有了一个健壮的异常安全承诺,即使T没有这个承诺。最终,我们过于加强了get()的承诺(这取决于value),而应该将它降低到T的承诺层次。我们用一个警告修正get()的承诺,基于我们不能控制或不能预知T的状态。In the end, we over-committed get's guarantee (the determinism of value), and had to bring it down to T's level. We amended get's contract with a caveat, based on conditions in T we couldn't control or predict.

原则:程序的健壮性等于它最弱的承诺。尽可能提供最健壮的承诺,同时在行为和接口上。

推论:如果你自己的接口的承诺比其他人的接口健壮,你通常必须将你的接口减弱到相匹配的程度。

1.2 Problem #3:set

我们现在的X::set()的实现是:

void set(T const &value)

{

*value_ = value;

}

(和get()不同,set()确实修改wrapper对象,所以不能申明为cosnt。)

语句

*value_ = value;

应该看起来很熟悉:她只是前面Problem #2中提到的语句

value = *value_;

的反序。注意到这个变化,Problem #3的解决方案就和Problem #2的一样了:bool set(T const &value) throw()

{

bool error(false);

try

{

*value = value_;

}

catch (...)

{

error = true;

}

return error;

}

和我们在get()中回传value遇到的问题一样:如果operator=抛了异常,我们无法知道*value_的状态。我们对get()的承诺的警告在这儿同样适用。

get()和set()现在有这同样的操作但不同的用途:get()将当前对象的值赋给另外一个对象,而set()将另外一个对象的值赋给当前对象。由于这种对称性,我们可以将共同的代码放入一个assign()函数:

static bool assign(T &to, T const &from) throw()

{

bool error(false);

try

{

to = from;

}

catch (...)

{

error = true;

}

return error;

}

使用了这个辅助函数后,get()和set()缩短为

bool get(T &value) const throw()

{

return assign(value, *value_);

}

bool set(T const &value) throw()

{

return assign(*value_, value);

}

1.3 最终版本

wrapper的最终版本是

template <typename T>

class wrapper

{

public:

wrapper() throw()

: value_(NULL)

{

try

{

value_ = new T;

}

catch (...)

{

}

}

~wrapper() throw()

{

try

{

delete value_;

}

catch (...)

{

operator delete(value_);

}

}

bool get(T &value) const throw()

{

return assign(value, *value_);

}

bool set(T const &value) throw()

{

return assign(*value_, value);

}

private:

bool assign(T &to, T const &from) throw()

{

bool error(false);

try

{

to = from;

}

catch (...)

{

error = true;

}

return error;

}

T *value_;

wrapper(wrapper const &);

wrapper &operator=(wrapper const &);

};

(哇!52行,原来只有20行的!而且这还只是一个简单的例子。)

注意,所有的异常处理函数只是吸收了那些异常而没有做任何处理。虽然这使得wrapper异常安全,却没有纪录下导致这些异常的原因。

我在Part13中讲的在构造函数上的相冲突的原则在这儿同样适用。异常安全是不够的,并且实际上是达不到预期目的的,如果它掩盖了最初的异常状态的话。同时,如果异常对象在被捕获前就弄死了程序的话,大部分的异常恢复方案都将落空。最后,良好的设计必须满足下两个原则:

l 通过异常对象的存在来注视异常状态,并适当地做出反应。

l 确保创造和传播异常对象不会造成更大的破坏。(别让治疗行为比病本身更糟糕。)

1.4 其它说法

在过去3部分中,我剖析了异常安全。我强烈建议你读一下这些文章:

l The first principles of C++ exception safety come from Tom Cargill's "Exception Handling: A False Sense of Security," originally published in the November and December 1994 issues of C++ Report. This article, more than any other, alerted us to the true complexities and subtleties of C++ exception handling.

l C++ Godfather Bjarne Stroustrup is writing an exception-safety Appendix for his book The C++ Programming Language (Third Edition) (http://www.research.att.com/~bs/3rd.html). Bjarne's offering a draft version (http://www.research.att.com/~bs/3rd_safe0.html) of that chapter on the Internet.

l I tend to think of exception safety in terms of contracts and guarantees, ideas formalized in Bertrand Meyer's "Design by Contract" (http://www.eiffel.com/doc/manuals/technology/contract/page.html) programming philosophy. Bertrand realizes this philosophy in both his seminal tome Object-Oriented Software Construction (http://www.eiffel.com/doc/oosc.html) and his programming language Eiffel (http://www.eiffel.com/eiffel/page.html).

l Herb Sutter has written the most thorough C++ exception-safety treatise I've seen. He's published it as Items 8-19 of his new book Exceptional C++ (http://www1.fatbrain.com/asp/bookinfo/bookinfo.asp?theisbn=0201615622). If you've done time on Usenet's comp.lang.c++.moderated newsgroup, you've seen Herb's Guru of the Week postings. Those postings inspired the bulk of his book. Highly recommended.

l Herb's book features a forward written by Scott Meyers. Scott covers exception safety in Items 9-15 of his disturbingly popular collection More Effective C++ (http://www1.fatbrain.com/asp/bookinfo/bookinfo.asp?theisbn=020163371X). If you don't have this book, you simply must acquire it; otherwise Scott's royalties could dry up, and he'd have to get a real job like mine.

Scott(在他的Item14)认为,不应该将异常规格申明加到模板成员上,和我的正相反。事实是无论用不用异常规格申明,总有一部分程序需要保护所有异常,以免程序自毁。Scott公正地指出不正确的异常规格申明将导致std::unexpected――这正是他建议你避开的东西;但,在本系列的Part11,我指出unexpected比不可控的异常传播要优越。

最后要说的是,这儿不会只有一个唯一正确的答案的。我相信异常规格申明可以导致更可预知和有限度的异常行为,即使是对于模板。我也得坦率地承认,在异常/模板混合体上我也没有足够经验,尤其是对大系统。我估计还很少有人有这种经验,因为(就我所知)还没有哪个编译器支持C++标准在异常和模板上的全部规定。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有