📄 item_075.htm
字号:
</style>
<!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"Table Normal";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Times New Roman";
mso-fareast-font-family:"Times New Roman";
mso-ansi-language:#0400;
mso-fareast-language:#0400;
mso-bidi-language:#0400;}
</style>
<![endif]--><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="3074"/>
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1"/>
</o:shapelayout></xml><![endif]-->
</head>
<body lang=ZH-CN link=blue vlink=purple style='tab-interval:36.0pt'>
<div class=Section1>
<h3><span lang=EN-US>75. </span><span style='font-family:宋体;mso-ascii-font-family:
Arial;mso-hansi-font-family:Arial'>避免异常规格(</span><span lang=EN-US>exception
specifications</span><span style='font-family:宋体;mso-ascii-font-family:Arial;
mso-hansi-font-family:Arial'>)</span></h3>
<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><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><a name=ch75index09></a><a name=ch75index08></a><a
name=ch75lev1sec2></a><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>
<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 lfo3;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 class=SpellE><span lang=EN-US>typedef</span></span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>一个指向函数的指针时</span></li>
<li class=MsoNormal style='mso-list:l1 level1 lfo3;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 class=SpellE><span
lang=EN-US>typedef</span></span></li>
<li class=MsoNormal style='mso-list:l1 level1 lfo3;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>override</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的虚函数使用了异常规格</span></li>
<li class=MsoNormal style='mso-list:l1 level1 lfo3;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>
<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/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
class=SpellE><span lang=EN-US>unexcepted_handler</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>terminate</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>UnknownException</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>function
templates</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>9</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>Java</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><a name=ch74lev1sec3></a></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>override</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>std::exception::what</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 style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
class=GramE><span lang=EN-US>class</span></span><span lang=EN-US> Base {<i
style='mso-bidi-font-style:normal'>// …<span style='mso-tab-count:1'> </span>//
</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><span
lang=EN-US><o:p></o:p></span></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
lang=EN-US><span style='mso-spacerun:yes'> </span>virtual f() </span><span
lang=EN-US style='font-family:"Arial Black"'>throw( X, Y, Z );</span><span
lang=EN-US><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><span lang=EN-US><o:p></o:p></span></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
lang=EN-US>};<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><span lang=EN-US>…<o:p></o:p></span></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
class=GramE><span lang=EN-US>class</span></span><span lang=EN-US> <span
class=SpellE>MyDerived</span> : public Base {<i style='mso-bidi-font-style:
normal'>// …<span style='mso-tab-count:1'> </span>// …</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><span
lang=EN-US>override<o:p></o:p></span></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
lang=EN-US><span style='mso-spacerun:yes'> </span>virtual f() </span><span
lang=EN-US style='font-family:"Arial Black"'>throw( X, Y, Z );</span><span
lang=EN-US><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><span lang=EN-US><o:p></o:p></span></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:225.0pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
lang=EN-US>};<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><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><span lang=EN-US>[<a
href="mk:@MSITStore:F:\SGuy\Books\Reading\Addison%20Wesley%20-%20C++%20Coding%20Standards%20-%20101%20Rules%20Guidelines.chm::/0321113586/biblio01.html#biblio01entry23"><span
style='color:windowtext;text-decoration:none;text-underline:none'>BoostLRG</span></a>]</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的经验是:对一个非内联的函数使用不抛出任何异常的异常规格(即:</span><span lang=EN-US>throw()</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></p>
</div>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -