📄 item_072.htm
字号:
style='mso-bidi-font-style:normal'>// </i></span><i style='mso-bidi-font-style:
normal'><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>构造一个对象</span></i></p>
<p class=MsoNormal style='margin-left:54.0pt;text-indent:18.0pt;tab-stops:288.0pt'><span
lang=EN-US>if( <span class=SpellE>SomeType::ConstructionWasOk</span>() ) {<span
style='mso-tab-count:1'> </span><i
style='mso-bidi-font-style:normal'>// </i></span><i style='mso-bidi-font-style:
normal'><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>测试构造是否成功</span></i></p>
<p class=MsoNormal style='margin-left:54.0pt;text-indent:18.0pt'><i
style='mso-bidi-font-style:normal'><span lang=EN-US>// …<o:p></o:p></span></i></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal style='margin-left:36.0pt;text-indent:-18.0pt;mso-list:l0 level1 lfo3;
tab-stops:list 36.0pt'><![if !supportLists]><span lang=EN-US style='font-family:
Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family:Symbol'><span
style='mso-list:Ignore'><span style='mso-spacerun:yes'> </span><span
style='font:7.0pt "Times New Roman"'>
</span></span></span><![endif]><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>那么得到的结果不仅难看,容易出错,而且会导致非法对象,这些对象并不真正满足自身类型的不变性——不用担心在多线程应用程序中调用</span><span
class=SpellE><span lang=EN-US>SomeType::ConstructionWasOk</span></span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>时必然会遇到的竞态条件(</span><span lang=EN-US>race condition</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>)。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>异常处理主要潜在的缺点在于:它需要程序员熟悉一些新的惯用法(</span><span
lang=EN-US>recurring idiom</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>),这些惯用法起源于异常所导致的非正常控制流。例如,析构函数和资源释放函数必须不会失败(参见第</span><span
lang=EN-US>51</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条),中间层的代码在遇到异常时必须正确(参见第</span><span
lang=EN-US>71</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条以及参考资料);要做到后一点,一个常见的编程惯用法就是先做所有可能抛出异常的工作,以保证安全,然后在真正的工作成功做完后,就可以只用那些提供了无错安全保证(</span><span
lang=EN-US>no-fail guarantee</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>)(参见第</span><span
lang=EN-US>51</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条及</span><span lang=EN-US>[Sutter00]</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>)的操作来提交并改变程序的状态。但是使用错误码也有它自己的惯用法;它们存在的时间更长,知道的人也更多,但不幸的是,人们一般习惯于忽略它们。<i
style='mso-bidi-font-style:normal'>注意后果自负。</i></span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>性能通常并不是异常处理的缺点。首先,要注意应该始终打开编译器的异常处理开关,即使默认情况下是关闭的,否则就无法得到标准的行为和错误报告,它们既可能来自</span><span
lang=EN-US>C++</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>语言的操作,如</span><span lang=EN-US>operator
new</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>,也可能来自标准库的操作,如</span><span lang=EN-US>STL</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>容器的插入操作(参见例外)。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span lang=EN-US><span
style='mso-spacerun:yes'> </span>[</span><span style='font-family:宋体;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>旁注:可以这样实现异常处理:在打开时使可执行文件的大小增加(这是不可避免的),但在没有抛出异常的情况下,不会或几乎不会引起性能开销,许多编译器是这样的。另外一些编译器确实会引起一些开销,尤其是在安全模式下,这是为了防止恶意代码通过缓冲区溢出来攻击异常处理机制。无论是否存在开销,都应该打开编译器对异常处理的支持,否则</span><span
lang=EN-US>C++</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>语言和标准库将无法正确地报告错误。我们提到这一点是因为我们知道有一些关闭了异常处理的项目,这是一个重要且影响深远的决定,除非是万不得已而且要极度小心,否则绝对不应该做出这样的决定。</span><span
lang=EN-US>]</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>一旦打开了编译器对异常处理的支持,在正常情况下(没有错误发生的情况)抛出异常和返回错误码的性能差异一般来说是可以忽略不计的。在的确发生错误的情况下,你可能会注意到性能的差异,但如果你频繁地抛出异常以致于处理异常抛出</span><span
lang=EN-US>/</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>捕获所引起的性能开销很显著,那么几乎可以肯定你在不应该使用异常的情况下使用了异常,这些情况不是真正的错误,因此你未能正确的区分错误与非错误(参见第</span><span
lang=EN-US>70</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条)。假设这些情况是真正的错误,确实违反了了前置和后置条件以及不变性,如果它们发生得如此频繁,那么应用程序肯定有严重的问题。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>过度使用错误码的一个症状是应用程序需要不断地检查各种条件,这些条件中的大部分始终成立,检查它们显然无关紧要,或者(更糟)没有检查它应该检查的错误码。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>过度使用异常的一个症状是应用程序频繁地抛出和捕获异常,以致于</span><span
lang=EN-US>try</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>代码块成功和失败的数量在同一个数量级上。像这样的</span><span
lang=EN-US>catch</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>代码块要么是没有处理真正的错误(违反了前置条件,后置条件,和不变性的错误),要么就是程序有严重的问题。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><b style='mso-bidi-font-weight:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>示例</span></b></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>示例</span><span lang=EN-US>1</span></i><i style='mso-bidi-font-style:
normal'><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>:构造函数(不变性错误)。</span></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>如果构造函数无法成功地创建一个该类型的对象,也就是说它无法确立新对象的不变性,那么它应该抛出异常。反过来说,构造函数抛出异常总是意味着对象的构造失败了,对象的生命期从未开始过;</span><span
lang=EN-US>C++</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>语言对些做了强制。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>示例</span><span lang=EN-US>2</span></i><i style='mso-bidi-font-style:
normal'><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>:成功地完成对树的递归搜索。</span></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>在用递归算法对树进行搜索时,可能会诱使你把搜索结果作为异常抛出,以此来返回结果,同时可以方便地展开搜索栈。但是请不要这样做:异常意味着错误,而找到结果不是错误(参见</span><span
lang=EN-US>[Stroustrup00]</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>)。(当然,还要注意在搜索函数的上下文中,没有找到结果也并不是错误;参见第</span><span
lang=EN-US>70</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条中的</span><span class=SpellE><span
lang=EN-US>find_first_of</span></span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>示例。)</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>另外请参见第</span><span lang=EN-US>70</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>条的示例,把“报告错误”替换为“抛出异常”。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><b style='mso-bidi-font-weight:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>例外</span><span lang=EN-US><o:p></o:p></span></b></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>在极少数情况下,如果你确信下面两条都成立,那么可以考虑使用错误码:</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<ul style='margin-top:0cm' type=disc>
<li class=MsoNormal style='mso-list:l1 level1 lfo1;tab-stops:list 36.0pt'><i
style='mso-bidi-font-style:normal'><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>异常所带来的好处并不适用:</span></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>例如,你知道几乎所有的直接调用方必须立即处理错误,因此错误传递绝不会或几乎不会发生。这种情况非常罕见,因为通常被调用方无法知道它所有的调用方的信息。</span></li>
</ul>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<ul style='margin-top:0cm' type=disc>
<li class=MsoNormal style='mso-list:l1 level1 lfo1;tab-stops:list 36.0pt'><i
style='mso-bidi-font-style:normal'><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>和使用错误码相比,抛出异常的实测性能确实有影响:</span></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>也就是说,如果性能差异是根据经验得出的,那么你可能在循环的内部,并且经常抛出异常;后一种比较罕见的,因为这通常意味着那种情况可能并不是错误,不过我们假设因为某种原因它是错误。</span></li>
</ul>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>在非常罕见的情况下,一些艰难的实时项目可能会面临这样的压力:由于所使用的编译器的异常处理机制的时间保证是最差的,这使得对一些关键操作来说,要达到规定的要求非常困难或不可能,因此不得不考虑完全关闭异常处理。当然,关闭异常处理就意味着</span><span
lang=EN-US>C++</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>语言和标准库不会以标准的方式报告错误(或者在某些情况下根本就不报告错误;请参阅编译器的文档),项目自己的错误报告机制就只能用错误码来取代异常处理。我们非常不希望关闭异常处理,这应该是万不得已的手段,无论怎么描述都不为过。在做决定之前,请仔细分析你准备如何从构造函数和操作符中报告错误,以及该机制确实会在你使用的编译器上正常运作。如果经过慎重考虑和深入分析,你仍然觉得必须关闭异常处理,那么也不要在整个项目中这样做:只在尽可能少的模块中这样做,尽量把这类关键操作集中到同一个模块中会有助于此。</span></p>
</div>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -