📄 item_030.htm
字号:
<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><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>&&</span></b><span style='font-family:宋体;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>和</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>||</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>先求值左边的表达式,如果那样就可以完全确定整个表达式的结果(对</span><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>&&</span></b><span style='font-family:宋体;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>来说为</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>false</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>,对</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>||</span></b><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>来说为</span><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>true</span></b><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='text-indent:21.0pt'><span lang=EN-US>Employee* e = <span
class=GramE>TryToGetEmployee(</span>);</span></p>
<p class=MsoNormal style='text-indent:21.0pt'><span class=GramE><span
lang=EN-US>if(</span></span><span lang=EN-US> <b style='mso-bidi-font-weight:
normal'>e && e->Manager()</b> )</span></p>
<p class=MsoNormal style='text-indent:21.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span><i style='mso-bidi-font-style:normal'>//
…<o:p></o:p></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><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>e</span></b><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><span style='font-family:
宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>则不会对</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>e->Manager()</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>求值。这是完全符合惯例的也没有任何问题——除非用到的</span><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>&&</span></b><span style='font-family:宋体;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>是一个被重载的</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>operator&&</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>,因为那样的话涉及到</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>&&</span></b><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 lfo3;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><span
lang=EN-US><o:p></o:p></span></i></li>
<li class=MsoNormal style='mso-list:l0 level1 lfo3;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><span
lang=EN-US>31</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><o:p></o:p></span></i></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 style='text-indent:21.0pt'><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>some_smart_ptr</span></b><span lang=EN-US><Employee>
e = <span class=GramE>TryToGetEmployee(</span>);</span></p>
<p class=MsoNormal style='text-indent:21.0pt'><span class=GramE><span
lang=EN-US>if(</span></span><span lang=EN-US> <b style='mso-bidi-font-weight:
normal'>e && e->Manager()</b> )</span></p>
<p class=MsoNormal style='text-indent:21.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span><i style='mso-bidi-font-style:normal'>//
…<o:p></o:p></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><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>operator&&</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>(由</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>some_smart_ptr</span></b><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>或</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>Employee</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的作者提供)。这时对阅读者来说,代码看起来仍然没有问题,但是当</span><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>e</span></b><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><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>时会潜在地(也是灾难性地)调用</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>e->Manager()</span></b><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>eager
evaluation</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>)的情况下,有些其它的代码并不会引起核心转储(</span><span
lang=EN-US>dump core</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='text-indent:21.0pt'><span class=GramE><span
lang=EN-US>if(</span></span><span lang=EN-US> <b style='mso-bidi-font-weight:
normal'>DisplayPrompt() && GetLine()</b> )</span></p>
<p class=MsoNormal style='text-indent:21.0pt'><i style='mso-bidi-font-style:
normal'><span lang=EN-US><span style='mso-spacerun:yes'> </span>// …<o:p></o:p></span></i></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><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>operator&&</span></b><span style='font-family:
宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>是一个用户定义的操作符,那么</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>DisplayPrompt</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>或</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>GetLine</span></b><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>build</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><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>&&</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>和</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>||</span></b><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>相似,内建的逗号操作符保证会按从左到右的顺序求值(与</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>&&</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>和</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>||</span></b><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>不同的是,它始终会对两边求值)。用户定义的逗号操作符无法保证按从左到右的顺序求值,其结果通常令人惊讶。例如,如果下面的代码调用一个用户定义的逗号操作符,那么无法确定</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>g</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>得到的是</span><span lang=EN-US>0</span><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><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='text-indent:21.0pt'><span class=GramE><span
lang=EN-US>int</span></span><span lang=EN-US> i = 0;</span></p>
<p class=MsoNormal style='text-indent:21.0pt'><span lang=EN-US>f( i ), g( i++
);<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>31</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><o:p></o:p></span></i></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><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>operator,</span></b></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><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>vector<string></span></b><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='text-indent:21.0pt'><span lang=EN-US>set_cont(letters)
+= "a", "b";<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 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='text-indent:21.0pt'><span lang=EN-US>set_cont(letters)
+= getstr(), getstr();<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>,</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><o:p></o:p></span></i></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><b style='mso-bidi-font-weight:
normal'><span lang=EN-US>getstr</span></b><span style='font-family:宋体;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>从控制台得到了用户的输入,而用户依次输入了字符串</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>"c"</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>和</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>"d"</span></b><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>,那么实际上字符串可能会以任意的顺序被使用。这是令人惊讶的,因为对内建的顺序</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>operator,</span></b><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='text-indent:21.0pt'><span lang=EN-US>string s; s =
getstr(), getstr();<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>,</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></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><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>一个例外是表达式模板程序库(</span><span lang=EN-US>expression
template library</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 + -