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

📄 item_070.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></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>See [Stroustrup00] §E.2.</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><span
class=SpellE><span lang=EN-US>vector::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>68</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>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></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>ifstream</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>14</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条)。</span></p>

<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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><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><span class=SpellE><span
lang=EN-US>std::string::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>string</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的指定位置时,调用方应该检查</span><span lang=EN-US>pos</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的有效性,例如</span><span lang=EN-US>pos &gt; size()</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></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><span lang=EN-US> <span
class=SpellE>std::string::append</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></p>

<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>对一个返回值的函数,产生一个有效的返回值对象是一个后置条件。如果无法正确地创建返回值(例如:如果函数返回</span><span
lang=EN-US>double</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>类型,但却不存在与得到的结果相对应的</span><span
lang=EN-US>double</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>值),那么这是一个错误。</span></p>

<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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><span lang=EN-US> <span
class=SpellE>std::string::find_first_of</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>string</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>类,这不能算是错误。如果根据更高层的不变性,该字符串的所有者认为该字符应该存在,若不存在则是一个错误,那么更高层的调用方代码可以根据它的不变性以适当的方式报告此错误。</span></p>

<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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>5</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>File</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>File::Write</span></span><span lang=EN-US>( const char* buffer, <span
class=SpellE>size_t</span> size )</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>要求缓冲区不为空,并且已经以写方式打开了文件,你可能决定在函数中做以下这些:</span></p>

<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span lang=EN-US><span
style='mso-spacerun:yes'>&nbsp;</span><o:p></o:p></span></i></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>buffer</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
lang=EN-US>NULL</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><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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>File</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><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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></i><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>报告错误,违反了后置条件,因为函数不能完成它所承诺要完成的工作。</span></p>

<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span lang=EN-US><o:p>&nbsp;</o:p></span></i></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>6</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
class=SpellE><span lang=EN-US>std::vector</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>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>at</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[]</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>无需验证它的参数,或者说无需在参数无效时也是安全的,所以必须在文档中说明调用方要自己负责确保参数在有效的范围内。这个函数本质上不怎么安全。另一方面,文档说明了即使出现了无效的参数</span><span
lang=EN-US>at</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>也会是安全的,如果</span><span lang=EN-US>at</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::out_ou_range</span></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 + -