📄 item_062.htm
字号:
<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><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><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 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><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>
<ul style='margin-top:0cm' type=disc>
<li class=MsoNormal style='mso-list:l0 level1 lfo1;tab-stops:list 36.0pt'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>在</span><span lang=EN-US>main</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><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></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:l0 level1 lfo1;tab-stops:list 36.0pt'><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></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:l0 level1 lfo1;tab-stops:list 36.0pt'><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:l0 level1 lfo1;tab-stops:list 36.0pt'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>在模块的接口的周围:你的子系统会暴露一些公有接口给外界使用。如果子系统是被打包为一个单独的库,那么最好把异常限制在内部,并用虽然老,但是可靠的错误码向外界报告错误。(参见第</span><span
lang=EN-US>72</span><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:l0 level1 lfo1;tab-stops:list 36.0pt'><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></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><span lang=EN-US>72</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 API</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>来说可以用错误码)。虽然这两种策略可能碰巧相同,但通常它们并不相同。错误处理策略只有在跨越模块的边界时才可以改变。要搞清楚如何在模块之间协调不同的策略(例如:如何与</span><span
lang=EN-US>COM</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>或</span><span lang=EN-US>CORBA</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 API</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>中捕获异常)。一个不错的解决方案是定义一个主要的函数,用来在异常和子系统返回的错误码间进行转换。这样,就能很容易地把对等模块(</span><span
lang=EN-US>peer module</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>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>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>72</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>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>catch(...)</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>catch(...)</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>Ei</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>catch(E1&) {/*…*/ }catch(E2&) {/*…*/ }… catch(En&)
{/*…*/ }</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><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><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><span
lang=EN-US>74</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><span
lang=EN-US>try/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>
</div>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -