⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 item_071.htm

📁 C++程序编写规范,适合C++中级读者
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<p class=MsoNormal><span lang=EN-US><o:p>&nbsp;</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>nothrow</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>[Abrahams96]</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 class=SpellE>GotW</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>[Stroustrup00] §E.2</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"'>中发表。由于它们适用于所有的错误处理,与具体的处理方法无关,因此我们一般用它们来描述错误处理的安全性。无错安全保证是强安全保证的真超集,而强安全保证又是基本安全保证的真超集。</span></p>

<p class=MsoNormal><span lang=EN-US><o:p>&nbsp;</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>vector::insert</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>&nbsp;</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>swap</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></p>

<p class=MsoNormal><span lang=EN-US><o:p>&nbsp;</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>int</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>42</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>被改为</span><span lang=EN-US>43</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>int</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>++</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>操作会产生</span><span lang=EN-US>43</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>而不是</span><span lang=EN-US>44</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>5</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>&nbsp;</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>copy
assignment operator</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>)要依赖于检查自赋值才能正常工作,那么要留意该函数。能够安全地处理错误的复制赋值操作符应该对自赋值也是安全的。把自赋值检查作为一项优化措施来避免不必要的处理是完全合理的(参见第</span><span
lang=EN-US>55</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>&nbsp;</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>&nbsp;</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></p>

<p class=MsoNormal><span lang=EN-US><o:p>&nbsp;</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></p>

<p class=MsoNormal><span lang=EN-US><o:p>&nbsp;</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>3</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><span class=SpellE><span
lang=EN-US>std::vector::insert</span></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>vector&lt;T&gt;</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的内部数据存储是连续的,所以在中间插入元素需要把一些已有的元素依次往后挪动一个位置,给新的元素腾出空间。挪动的操作是通过</span><span
lang=EN-US>T::T(const T&amp;)</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>T::operator</span></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>insert</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>提供强安全保证的唯一方法就是构造一个该容器的完整复件,在复件上进行操作,如果操作成功,就用提供了无错安全保证的</span><span
lang=EN-US>vector&lt;T&gt;::swap</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>&nbsp;</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>insert</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>vector::insert</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>&nbsp;</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>4</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>f</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>LaunchSatellite</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>f</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>可以在发射人造卫星之前执行所有可能会失败的操作,就可以使</span><span
lang=EN-US>f</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>提供强安全保证。但是如果</span><span lang=EN-US>f</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>必须在已经发射之后再执行一些可能会失败的操作,那么</span><span lang=EN-US>f</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>就无法提供强安全保证,因为无法把人造卫星收回。(无论如何,像</span><span lang=EN-US>f</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>这样的函数可能应该被拆分为两个函数,因为一个单独的函数或许不应该执行多个如此重要的操作;参见第</span><span
lang=EN-US>5</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 + -