📄 item_015.htm
字号:
<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 style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>常量简化了代码,因为只需看一下该常量的定义就可以知道它在各处的值了。考虑以下代码:</span></p>
<p class=MsoNormal style='margin-left:36.0pt'><span class=GramE><span
lang=EN-US>void</span></span><span lang=EN-US> Fun( vector<<span
class=SpellE>int</span>>& v ) {</span></p>
<p class=MsoNormal style='margin-left:36.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span>// ...</span></p>
<p class=MsoNormal style='margin-left:36.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span><span class=GramE>const</span> <span
class=SpellE>size_t</span> <span class=SpellE>len</span> = <span class=SpellE>v.size</span>();</span></p>
<p class=MsoNormal style='margin-left:36.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span>// ... <i style='mso-bidi-font-style:
normal'>30 more <span class=GramE>lines ...</span></i></span></p>
<p class=MsoNormal style='margin-left:36.0pt'><span lang=EN-US>}</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>len</span></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>len</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>const</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>,它也不应该这么做,参见下面):它是</span><span
lang=EN-US>v</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>len</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>const</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>len</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>const</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>X*</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>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>,那么</span><span lang=EN-US>X*</span><span style='font-family:
宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>也是</span><span
lang=EN-US>const</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>——但该指针指向的</span><span lang=EN-US>X</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>对象不是。(参见</span><span lang=EN-US>[Saks99]</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>mutable</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>成员来实现逻辑上的不变性。如果一个类的某个</span><span lang=EN-US>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>成员函数有正当地理由要修改某个成员变量(也就是说,该成员变量不影响对象的外部可见状态,例如用于高速缓存的数据),那么就将该成员变量声明为</span><span
lang=EN-US>mutable</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>Pimpl</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>mutable</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>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>是“病毒性”的——一旦在某处加入,当调用其它尚未声明为</span><span lang=EN-US>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的函数时,它就会主动传播到代码中的各处。这是语言的特性,而不是错虫,虽然在</span><span
lang=EN-US>const</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>还未被正确理解和重视的时候,它曾被不公正地贬低过,但是这个性质极大地增强了</span><span
lang=EN-US>const</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的作用。要更新已有的代码,使之正确地使用</span><span
lang=EN-US>const</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>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>是值得的,有效的,已经被证实,笔者极力推荐。理解程序的状态在何时及何处改变是至关重要的,而</span><span
lang=EN-US>const</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>直接在代码中做了说明,并能借助于编译器加以强制。恰当地使用</span><span
lang=EN-US>const</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>有助于更好地理解设计,并使代码更健壮,更安全。若发现无法使某个成员函数成为</span><span
lang=EN-US>const</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>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>函数,或者在极少的情况下为了绕开一些旧编译器缺乏对</span><span lang=EN-US>mutable</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的支持,否则绝对不要强制去除</span><span lang=EN-US>const</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></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>const</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 class=GramE><span lang=EN-US>void</span></span><span
lang=EN-US> <span class=SpellE>Func</span>( <span class=SpellE>int</span> x );</span></p>
<p class=MsoNormal><span lang=EN-US>void <span class=SpellE>Func</span>( </span><span
lang=EN-US style='font-family:"Arial Black"'>const</span><span lang=EN-US> <span
class=SpellE>int</span> x );<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>const</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 style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>在第二个声明中,</span><span lang=EN-US>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>是多余的。我们建议在声明函数时不要使用最高层的</span><span lang=EN-US>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>,这样阅读头文件的人就不会搞糊涂了。但是,最高层的</span><span lang=EN-US>const</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>在函数定义中确实有很大的不同,因此如果用来捕获对参数的无意更改也是合理的:</span></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:180.0pt'><span
lang=EN-US>void Fun( const <span class=SpellE>int</span> x ) {<span
style='mso-tab-count:1'> </span><i
style='mso-bidi-font-style:normal'>// Fun</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></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:180.0pt'><span
lang=EN-US><span style='mso-spacerun:yes'> </span>// ...</span></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:180.0pt'><span
lang=EN-US><span style='mso-spacerun:yes'> </span>++x;<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></i></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:180.0pt'><span
lang=EN-US><span style='mso-spacerun:yes'> </span>// ...</span></p>
<p class=MsoNormal style='margin-left:36.0pt;tab-stops:180.0pt'><span
lang=EN-US>}</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></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 + -