﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-归去来兮</title><link>http://www.cppblog.com/xiaohe0817/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 14 Apr 2026 23:09:10 GMT</lastBuildDate><pubDate>Tue, 14 Apr 2026 23:09:10 GMT</pubDate><ttl>60</ttl><item><title>Effective C++读书笔记之三 :确定对象被使用前已先被初始化</title><link>http://www.cppblog.com/xiaohe0817/archive/2008/12/30/70572.html</link><dc:creator>Edmund</dc:creator><author>Edmund</author><pubDate>Tue, 30 Dec 2008 03:06:00 GMT</pubDate><guid>http://www.cppblog.com/xiaohe0817/archive/2008/12/30/70572.html</guid><wfw:comment>http://www.cppblog.com/xiaohe0817/comments/70572.html</wfw:comment><comments>http://www.cppblog.com/xiaohe0817/archive/2008/12/30/70572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xiaohe0817/comments/commentRss/70572.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xiaohe0817/services/trackbacks/70572.html</trackback:ping><description><![CDATA[关于对象初始化，c++似乎反复无常，例如：<br>int x；<br>在某些语境条件下保证初始化为0，而在另外一些语境下却并没有这个保证。而下边这种情况：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_11_22_Open_Image onclick="this.style.display='none'; Codehighlighter1_11_22_Open_Text.style.display='none'; Codehighlighter1_11_22_Closed_Image.style.display='inline'; Codehighlighter1_11_22_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_11_22_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_11_22_Closed_Text.style.display='none'; Codehighlighter1_11_22_Open_Image.style.display='inline'; Codehighlighter1_11_22_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Point</span><span id=Codehighlighter1_11_22_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_11_22_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x,y;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Point&nbsp;p;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
p的成员函数有的时候被初始化（0），有的时候没有被初始化。<br>现在，已经有了一些规则，对象的初始化何时会发生，何时不会发生。但是这些规则过于复杂，对我们的记忆有一定的挑战，哈哈。<br>通常如果使用C part of C++而且初始化可能招致运行期成本，那就不保证发生初始化。一旦进入non-C parts of C++,规则有一些变化。这就是为啥array(C part of C++)不保证其内容被初始化，而vector(STL part of C++)却有此保证。<br>我们的最佳处理状态是，在使用任何对象之前先将它初始化。对于无任何成员的内置类型，必须手动完成此事：
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">对int进行初始化；</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">text&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;A&nbsp;C-style&nbsp;string</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">对指针进行初始化；</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">double</span><span style="COLOR: #000000">&nbsp;d;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>std::cin&nbsp;</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">&nbsp;d;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">采用input&nbsp;stream的方式完成初始化；</span></div>
至于内置类型以外的其他类型，初始化的责任落在了构造函数身上。规则很简单：确保构造函数都将对象的每一个成员初始化。<br>这个规则很容易奉行，值得注意的是别混淆了赋值(assignment)和初始化(initialization)。考虑一个表现通讯簿的class：
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_17_21_Open_Image onclick="this.style.display='none'; Codehighlighter1_17_21_Open_Text.style.display='none'; Codehighlighter1_17_21_Closed_Image.style.display='inline'; Codehighlighter1_17_21_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_17_21_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_17_21_Closed_Text.style.display='none'; Codehighlighter1_17_21_Open_Image.style.display='inline'; Codehighlighter1_17_21_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;PhoneNumber</span><span id=Codehighlighter1_17_21_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_17_21_Open_Text><span style="COLOR: #000000">{<img src="http://www.cppblog.com/Images/dot.gif">}</span></span><span style="COLOR: #000000">;<br><img id=Codehighlighter1_37_281_Open_Image onclick="this.style.display='none'; Codehighlighter1_37_281_Open_Text.style.display='none'; Codehighlighter1_37_281_Closed_Image.style.display='inline'; Codehighlighter1_37_281_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_37_281_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_37_281_Closed_Text.style.display='none'; Codehighlighter1_37_281_Open_Image.style.display='inline'; Codehighlighter1_37_281_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ABEntry</span><span id=Codehighlighter1_37_281_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_37_281_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Address&nbsp;Book&nbsp;Entry;</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>ABEntry(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">name,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;&amp;address,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">PhoneNumber</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">phones);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;theName;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;tehAddress;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">PhoneNumber</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;thePhones;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;numTimesConsulted;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>ABEntry::ABEntry(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">name,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;&amp;address,&nbsp;std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">PhoneNumber</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">phones)<br><img id=Codehighlighter1_386_511_Open_Image onclick="this.style.display='none'; Codehighlighter1_386_511_Open_Text.style.display='none'; Codehighlighter1_386_511_Closed_Image.style.display='inline'; Codehighlighter1_386_511_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_386_511_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_386_511_Closed_Text.style.display='none'; Codehighlighter1_386_511_Open_Image.style.display='inline'; Codehighlighter1_386_511_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_386_511_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_386_511_Open_Text><span style="COLOR: #000000">{</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">以下全是赋值(assignment)，不是初始化(initialization);</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">theName&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;name;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>theAddress&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;address;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>thePhones&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;phones;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>numTimesConsulted&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
ABEntry对象会带给你期望的值，但不是最佳做法。C++规定：对象成员变量的初始化动作发生在进入构造函数本体之前。在ABEntry构造函数内，theName, theAddress, thePhone都不是被初始化，而是被赋值。初始化的时间发生的更早，发生于这些成员函数的default构造函数被自动调用之时(比进入ABEntry构造函数本体的时间更早)。但这对numTimesConsulted不为真，因为他属于内置类型，不保证在你看到的那个赋值动作的时间点之前获得初值。<br>ABEntry构造函数的一个较好写法是，使用所谓的member initializatin list(成员函数初始列)替换赋值动作：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_183_184_Open_Image onclick="this.style.display='none'; Codehighlighter1_183_184_Open_Text.style.display='none'; Codehighlighter1_183_184_Closed_Image.style.display='inline'; Codehighlighter1_183_184_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_183_184_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_183_184_Closed_Text.style.display='none'; Codehighlighter1_183_184_Open_Image.style.display='inline'; Codehighlighter1_183_184_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #000000">ABEntry::ABEntry(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">name,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">address,<br>&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">PhoneNumber</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">phones)<br>:&nbsp;theName(name),<br>&nbsp;theAddress(address),<br>&nbsp;thePhones(phones),&nbsp;<br>numTimesConsulted(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)<br></span><span id=Codehighlighter1_183_184_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_183_184_Open_Text><span style="COLOR: #000000">{}</span></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">构造函数本体不需要任何动作</span></div>
这个构造函数和上一个的结果相同，但是通常效率较高。基于赋值的那个版本首先调用default构造函数为theName, theAddress,和thePhones设立初值，然后立刻再对他们赋值。default的一切作为此刻都浪费了。成员初始列(member initianlization list)的做法避免了这一问题，因为初始列中针对各个变量设的实参，被拿去作为各个成员函数的实参。本例中theName以name为初值进行copy构造，theAddress以address为初值进行copy构造，thePhones以phones为初值进行copy构造。<br>对于大多数类型而言，比起先调用default构造函数再调用copy assignment操作符，单只调用一次copy构造函数是比较高效的，有事甚至高的多。对于内置类型，如numTimesConsulted，其初始化成本和赋值的成本相同，但为了一致性最好也通过成员初始列来进行初始化。同样道理，如果我们想default构造一个成员变量，也可以使用成员初始列，只要指定nothing作为初始化列就行了，假设ABEntry有一个无参构造构造函数，我们可以实现如下：
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">ABEntry::ABEntry()<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>:theName(),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用theName的构造函数</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">theAddress(),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用theAddress的构造函数</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">thePhones(),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用thePhomes的构造函数</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">numTimesConsulted(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">记得将此内置类型显式定义为0</span><span style="COLOR: #008000"><br><img id=Codehighlighter1_188_189_Open_Image onclick="this.style.display='none'; Codehighlighter1_188_189_Open_Text.style.display='none'; Codehighlighter1_188_189_Closed_Image.style.display='inline'; Codehighlighter1_188_189_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_188_189_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_188_189_Closed_Text.style.display='none'; Codehighlighter1_188_189_Open_Image.style.display='inline'; Codehighlighter1_188_189_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_188_189_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_188_189_Open_Text><span style="COLOR: #000000">{}</span></span></div>
此处记得一条规则，那就是在成员初始列中列出所有的成员变量，以免还得记住哪些成员变量可以无需初值。举个例子，如numTimesConsulted属于内置类型，如果(member initialization list)遗漏了他，他就没有初值，因而可能开启&#8220;不明确行为&#8221;的潘多拉盒子。<br>有些情况下，即使面对的是内置类型，也一定要使用初始列。如果成员变量是const或者reference，他们一定得需要赋初值，而不是赋值。为避免成员变量何时必须在成员初始列中初始化，何时不需要，最简单的做法就是：总是使用成员初始列。<br>C++有着十分固定的&#8220;成员初始化次序&#8221;。是的，次序总是相同的：base classes总是先于derived classes被初始化，而class的成员变量总是以其声明次序被初始化。让我们在看一下ABEntry，其theName永远先被初始化，然后是theAddress，之后thePhones，最后是numTimesConsulted，即使他们在member initialization list中以不同的顺序出现，也不会有任何影响。为了避免迷惑，你在member initialization list中条列各个成员时，最好总是以其声明次序为次序。<br>一旦我们已经 很小心的将内置型成员变量明确的加以初始化，而且也确保构造函数运用member initialization list初始化base classes和成员变量，那就剩下唯一的一件事情需要操心，就是：不同编译单元内定义之non-local static对象的初始化次序。<br>所谓static对象，其寿命从被构造出来直到程序结束为止，因此stack和heap_based对象都被排除。这里所说的static对象包括global对象，定义于namespace对象内的对象，在class内，在函数内，以及在file内被声明为static的对象。函数内的static对象称为local static对象，其他则成为non-local static对象，程序结束时static对象会自动销毁，也就是他们的析构函数会在main()结束时被调用。<br>所谓编译单元（translation unit）是指产生单一目标文件（single object file）的那些源码。基本上是单一源码文件加上其所包含的头文件。<br>现在，上面所涉及到的问题至少包括两个源码文件，每一个内含至少一个non-local static对象（也就是说对象是global，或者位于namespace内，或者class内，或者file作用域内被声明为static）。问题是：如果某个编译单元内的某个non-local static对象的初始化动作使用了另外一个编译单元内的non-local static对象，他所使用的这个对象可能没有初始化，因为C++对&#8220;不同编译单元内的non-local static 对象&#8221;的初始化次序并没有明确定义。<br>来个例子：<br>&nbsp;假设有一个FileSystem class，它让互联网上的文件看起来像是本机。由于这个class使世界看起来像个单一文件系统，可能会产生一个特殊对象，位于global或namespace作用域内，象征单一文件系统：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_16_64_Open_Image onclick="this.style.display='none'; Codehighlighter1_16_64_Open_Text.style.display='none'; Codehighlighter1_16_64_Closed_Image.style.display='inline'; Codehighlighter1_16_64_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_16_64_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_16_64_Closed_Text.style.display='none'; Codehighlighter1_16_64_Open_Image.style.display='inline'; Codehighlighter1_16_64_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;FileSystem</span><span id=Codehighlighter1_16_64_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_16_64_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::size_t&nbsp;numDisks()&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">extern</span><span style="COLOR: #000000">&nbsp;FileSystem&nbsp;tfs;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">预备给客户使用的对象，tfs代表"the&nbsp;file&nbsp;system"</span></div>
FileSystem对象绝不是一个无关痛痒的对象，因此客户如果在theFileSystem对象构造完成前就使用他，会得到惨重的代价：<br>现在假设建立了一个class用以处理文件系统内部的目录。很自然，他们会用上theFileSystem的对象：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_15_45_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_45_Open_Text.style.display='none'; Codehighlighter1_15_45_Closed_Image.style.display='inline'; Codehighlighter1_15_45_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_15_45_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_45_Closed_Text.style.display='none'; Codehighlighter1_15_45_Open_Image.style.display='inline'; Codehighlighter1_15_45_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Directory</span><span id=Codehighlighter1_15_45_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_15_45_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>Directory(<img src="http://www.cppblog.com/Images/dot.gif">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Directory::Directory(<img src="http://www.cppblog.com/Images/dot.gif">)<br><img id=Codehighlighter1_74_120_Open_Image onclick="this.style.display='none'; Codehighlighter1_74_120_Open_Text.style.display='none'; Codehighlighter1_74_120_Closed_Image.style.display='inline'; Codehighlighter1_74_120_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_74_120_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_74_120_Closed_Text.style.display='none'; Codehighlighter1_74_120_Open_Image.style.display='inline'; Codehighlighter1_74_120_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_74_120_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_74_120_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::size_t&nbsp;disks&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;tfs.numDisks();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
进一步假设，这些客户决定创建一个directory对象，用来放置临时文件：<br>Directory tempDir(params);//为临时文件而做出的目录<br>现在，初始化的重要作用显示出来了，除非tfs在tempDir之前被初始化，否则tempDir的构造函数会用到尚未初始化的tfs。但是tfs和tempDir是不同的人在不同的时间创建出来的，他们是定义于不同编译单元内地non-local static对象，如何能确认tfs在tempDir之前先被初始化？<br>哦，这是无法确认的。<br>一个小小的设计可以改变这种情形：将每一个non-local static 对象搬到自己的专属函数内（该对象在此函数内被声明为static）。这些函数返回一个reference用来指向他所包含的对象。然后用户调用这些函数，而不是直接指涉这些对象。换句话就是non-local static对象被local static对象替换了。这个实现的基础在于：C++保证，函数内的non-local static 对象会在函数被调用期间首次遇上该对象之定义式时被初始化。所以如果以函数调用替换直接访问non-local static 对象，就获得了保证，保证所得的那个reference将指向一个经历初始化的对象。 此技术实现于上面的代码如下：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_16_20_Open_Image onclick="this.style.display='none'; Codehighlighter1_16_20_Open_Text.style.display='none'; Codehighlighter1_16_20_Closed_Image.style.display='inline'; Codehighlighter1_16_20_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_16_20_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_16_20_Closed_Text.style.display='none'; Codehighlighter1_16_20_Open_Image.style.display='inline'; Codehighlighter1_16_20_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;FileSystem</span><span id=Codehighlighter1_16_20_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_16_20_Open_Text><span style="COLOR: #000000">{<img src="http://www.cppblog.com/Images/dot.gif">}</span></span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">同前</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">FileSystem&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tfs()</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">这个函数用来替换tfs对象；他在FileSystem中可能是个static。</span><span style="COLOR: #008000"><br><img id=Codehighlighter1_85_120_Open_Image onclick="this.style.display='none'; Codehighlighter1_85_120_Open_Text.style.display='none'; Codehighlighter1_85_120_Closed_Image.style.display='inline'; Codehighlighter1_85_120_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_85_120_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_85_120_Closed_Text.style.display='none'; Codehighlighter1_85_120_Open_Image.style.display='inline'; Codehighlighter1_85_120_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_85_120_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_85_120_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;FileSystem&nbsp;fs;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;fs;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_137_141_Open_Image onclick="this.style.display='none'; Codehighlighter1_137_141_Open_Text.style.display='none'; Codehighlighter1_137_141_Closed_Image.style.display='inline'; Codehighlighter1_137_141_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_137_141_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_137_141_Closed_Text.style.display='none'; Codehighlighter1_137_141_Open_Image.style.display='inline'; Codehighlighter1_137_141_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Directory</span><span id=Codehighlighter1_137_141_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_137_141_Open_Text><span style="COLOR: #000000">{<img src="http://www.cppblog.com/Images/dot.gif">}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Directory::Directory(<img src="http://www.cppblog.com/Images/dot.gif">)<br><img id=Codehighlighter1_170_218_Open_Image onclick="this.style.display='none'; Codehighlighter1_170_218_Open_Text.style.display='none'; Codehighlighter1_170_218_Closed_Image.style.display='inline'; Codehighlighter1_170_218_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_170_218_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_170_218_Closed_Text.style.display='none'; Codehighlighter1_170_218_Open_Image.style.display='inline'; Codehighlighter1_170_218_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_170_218_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_170_218_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::size_t&nbsp;disks&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;tfs().numDisks();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Directory&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">tempDir()<br><img id=Codehighlighter1_241_275_Open_Image onclick="this.style.display='none'; Codehighlighter1_241_275_Open_Text.style.display='none'; Codehighlighter1_241_275_Closed_Image.style.display='inline'; Codehighlighter1_241_275_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_241_275_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_241_275_Closed_Text.style.display='none'; Codehighlighter1_241_275_Open_Image.style.display='inline'; Codehighlighter1_241_275_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_241_275_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_241_275_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;Directory&nbsp;td;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;td;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
修改之后，我们只是使用的是tfs()和tempDir()，而不是tfs和tempDir。也就是使用的是指向static的reference而不是static对象自身。<br>这样的reference-returning函数往往十分单纯：第一行定义并初始化一个local static对象，第二行返回他。当然，他是绝佳的inline候选，特别是经常被调用的话。但是，另一方面，任何一种non-const static对象，不论是local或者non-local，在多线程下&#8220;等待某事发生都会有麻烦的。处理这个麻烦的一种做法是：在程序的单线程启动阶段手工调用所有的reference-returning函数（？？），这可消除与初始化有关的race condition。<br>为避免对象初始化之前过早的使用他们，你需要做的是三件事：手工初始化内置的non-member对象；使用member initialization list；在初始化次序不确定下加强你的设计。<br><br><br>Things to remember:<br>1.Manually initialize objects of built-in type, because C++only sometimes initializes them itself;<br>2.In a constructor, prefer to use the member initialization list to assigenment inside the body of the constructor. List data member in the initialization list in the same order they're declared&nbsp; in the class.<br>3.Avoid initialization order problems across translation units by replacing non-local static objects with local static objects. 
<img src ="http://www.cppblog.com/xiaohe0817/aggbug/70572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xiaohe0817/" target="_blank">Edmund</a> 2008-12-30 11:06 <a href="http://www.cppblog.com/xiaohe0817/archive/2008/12/30/70572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Effective C++读书笔记之二 :尽可能使用const</title><link>http://www.cppblog.com/xiaohe0817/archive/2008/12/09/68989.html</link><dc:creator>Edmund</dc:creator><author>Edmund</author><pubDate>Tue, 09 Dec 2008 15:00:00 GMT</pubDate><guid>http://www.cppblog.com/xiaohe0817/archive/2008/12/09/68989.html</guid><wfw:comment>http://www.cppblog.com/xiaohe0817/comments/68989.html</wfw:comment><comments>http://www.cppblog.com/xiaohe0817/archive/2008/12/09/68989.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xiaohe0817/comments/commentRss/68989.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xiaohe0817/services/trackbacks/68989.html</trackback:ping><description><![CDATA[<p>条款三：尽可能使用const（use const whenever possible）<br>const允许你指定一个语义约束，而编译器会强制实施这项约束。它允许你告诉编译器和其他程序员某值应该保持不变。有一条约束需要注意，那就是：如果const出现在*号的左边，那就是说被指物是常量；如果出现在星号右边，则表示指针本身是常量；如果出现在两边，则被指物和指针都是常量。如果被指物是常量，则关键字const写在类型的前面和类型之后，星号之前两种所表示的语义是相同的。例如下面这两种写法是一样的：<br>void f1(const Widget* pw);<br>void f2(Widget const * pw);<br>const也可用来修饰STL中的迭代器。声明迭代器为const就想声明指针为const一样（即T* const 指针），表示这个迭代器不得指向不同的东西。但它所指的东西的值是可以改变的。如果希望迭代器所指的东西不可改变（即模拟一个const T*指针），需要的是const_iterator:<br>std::vector&lt;int&gt; vec;<br>...<br>const std::vector&lt;int&gt;::iterator iter = vec.begin();// same as T* const<br>*iter = 10;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//no problem<br>++iter;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//wrong!!<br>std::vector&lt;int&gt;::const_iterator cIter = vec.begin();//same as&nbsp;const T*<br>*iter = 10;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//wrong!!<br>++iter;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//no problem</p>
<p>const&nbsp;最具威力（？）的用法是面对函数声明时的应用。在一个函数声明式内，const可以和函数返回值，各参数，函数自身（成员函数）产生关联。<br>令函数返回一个常量值，往往可以降低因客户错误而造成的意外，而又不至于放弃安全性和高效性。例如，考虑有理数的operator*声明式：<br>class Rational(){...};<br>const Rational operator* (const Rational &amp; lhs, const Rational &amp; rhs);<br>也许你会说为什么返回一个const对象？原因是如果不这样别人可能实现这样的暴行：<br>Rational a,b,c;<br>...<br>(a*b)=c;<br>下面，主要说明const作用于成员函数。<br>许多人都忽视了这么一个事实，那就是如果两个成员函数只是常量性不同，那么他们是可以重载的。考虑以下这个用来表示一大块文字的class：<br></p>
<span id=Codehighlighter1_14_24_Open_Text>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_15_196_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_196_Open_Text.style.display='none'; Codehighlighter1_15_196_Closed_Image.style.display='inline'; Codehighlighter1_15_196_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_15_196_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_196_Closed_Text.style.display='none'; Codehighlighter1_15_196_Open_Image.style.display='inline'; Codehighlighter1_15_196_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;TextBlock</span><span id=Codehighlighter1_15_196_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_15_196_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">[](std::size_t&nbsp;position)&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_80_103_Open_Image onclick="this.style.display='none'; Codehighlighter1_80_103_Open_Text.style.display='none'; Codehighlighter1_80_103_Closed_Image.style.display='inline'; Codehighlighter1_80_103_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_80_103_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_80_103_Closed_Text.style.display='none'; Codehighlighter1_80_103_Open_Image.style.display='inline'; Codehighlighter1_80_103_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_80_103_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_80_103_Open_Text><span style="COLOR: #000000">{</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;text[position];}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">[](std::size_t&nbsp;position)<br><img id=Codehighlighter1_144_167_Open_Image onclick="this.style.display='none'; Codehighlighter1_144_167_Open_Text.style.display='none'; Codehighlighter1_144_167_Closed_Image.style.display='inline'; Codehighlighter1_144_167_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_144_167_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_144_167_Closed_Text.style.display='none'; Codehighlighter1_144_167_Open_Image.style.display='inline'; Codehighlighter1_144_167_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_144_167_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_144_167_Open_Text><span style="COLOR: #000000">{</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;text[position];}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;text;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>TextBlock的operator[]可以这么使用：<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>TextBlock&nbsp;tb(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>std::cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;tb[</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">];&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用non-const&nbsp;</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;TextBlock&nbsp;ctb(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>std::cont&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;ctb[</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">];&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">调用const<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<br>真是情形中const对象多用于passed by pointer-to-const或passed by reference-to-const的传递结果。上述的ctb太过于造作，下边这个比较真实：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;print&nbsp;(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;TextBlocd</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;ctb)<br><img id=Codehighlighter1_34_65_Open_Image onclick="this.style.display='none'; Codehighlighter1_34_65_Open_Text.style.display='none'; Codehighlighter1_34_65_Closed_Image.style.display='inline'; Codehighlighter1_34_65_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_34_65_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_34_65_Closed_Text.style.display='none'; Codehighlighter1_34_65_Open_Image.style.display='inline'; Codehighlighter1_34_65_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_34_65_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_34_65_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;std::cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;ctb[</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
<br>只用重载operator[]并对不同的版本给予不同的返回类型，就可以令const和non-const获得不同的处理。<br>此处需要注意一点，non-const operator[]的返回类型是个reference to char,不是char。如果operator[]返回的是个char，下边的赋值就不能通过编译：<br>tb[0] = 'x'; //error c2106: ' = ' : left operand must be l-value<br>那是因为，如果函数的返回类型是个内置类型，那么改动函数的返回值从来就不合法。纵使合法，C++以by value返回对象这一事实（条款20）意味着改动的其实只是tb.text[0]的一个副本，不是tb.text[0]本身，那不是我们想要的结果。<br>下边来说说在const和non-const成员函数中避免重复<br>假设TextBlock（和CTextBlock）内的operator[]不单只是返回一个reference指向某字符，也执行边界检查、志记访问信息、甚至可能进行数据完整性检验。把所有这些同时放进const和non-const operator[]中，导致这样的一个怪物：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_15_420_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_420_Open_Text.style.display='none'; Codehighlighter1_15_420_Closed_Image.style.display='inline'; Codehighlighter1_15_420_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_15_420_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_420_Closed_Text.style.display='none'; Codehighlighter1_15_420_Open_Image.style.display='inline'; Codehighlighter1_15_420_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;TextBlock</span><span id=Codehighlighter1_15_420_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_15_420_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">[](std::size_t&nbsp;position)&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_80_215_Open_Image onclick="this.style.display='none'; Codehighlighter1_80_215_Open_Text.style.display='none'; Codehighlighter1_80_215_Closed_Image.style.display='inline'; Codehighlighter1_80_215_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_80_215_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_80_215_Closed_Text.style.display='none'; Codehighlighter1_80_215_Open_Image.style.display='inline'; Codehighlighter1_80_215_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_80_215_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_80_215_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">边界检查(bounds&nbsp;checking)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">志记数据访问(log&nbsp;access&nbsp;data)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">检验数据完整性(verify&nbsp;data&nbsp;integrity)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;text[position];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">[](std::size_t&nbsp;position)<br><img id=Codehighlighter1_256_391_Open_Image onclick="this.style.display='none'; Codehighlighter1_256_391_Open_Text.style.display='none'; Codehighlighter1_256_391_Closed_Image.style.display='inline'; Codehighlighter1_256_391_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_256_391_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_256_391_Closed_Text.style.display='none'; Codehighlighter1_256_391_Open_Image.style.display='inline'; Codehighlighter1_256_391_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_256_391_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_256_391_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">边界检查(bounds&nbsp;checking)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">志记数据访问(log&nbsp;access&nbsp;data)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">检验数据完整性(verify&nbsp;data&nbsp;integrity)</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;text[position];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;text;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;</span></div>
其中代码的代码重复性及伴随的编译时间，维护，代码膨胀等问题真是令人头疼啊。当然了，将边界检查&#8230;&#8230;等所有代码移植到另一个成员函数，并令两个版本的operator[]调用它，是可能的，但是还是重复了一些代码，例如函数调用，两次return语句等。<br>我们真正要做的，是实现operator[]的机能一次并使用它两次。也就是说，你必须使一个调用另一个。这促使我们将常量性转除（casting away constness）。<br>就一般而言，casting是一个糟糕的想法，在条款27中有详细的说明。然而代码重复也不是什么令人愉快的经验。本例中cosnt operator[]完全做掉了non-const版本该做的一切，唯一不同是其返回类型多了一个const资格修饰。这种情况下如果将返回值的const转除是安全的，因为不论谁调用non-const operator[]都一定首先有个non-const对象，否则就不能够调用non-const函数。所以令non-const operator[]调用其const兄弟是一个避免重复的安全做法：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_15_234_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_234_Open_Text.style.display='none'; Codehighlighter1_15_234_Closed_Image.style.display='inline'; Codehighlighter1_15_234_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_15_234_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_234_Closed_Text.style.display='none'; Codehighlighter1_15_234_Open_Image.style.display='inline'; Codehighlighter1_15_234_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;TextBlock</span><span id=Codehighlighter1_15_234_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_15_234_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">[](std::size_t&nbsp;position)&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_80_117_Open_Image onclick="this.style.display='none'; Codehighlighter1_80_117_Open_Text.style.display='none'; Codehighlighter1_80_117_Closed_Image.style.display='inline'; Codehighlighter1_80_117_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_80_117_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_80_117_Closed_Text.style.display='none'; Codehighlighter1_80_117_Open_Image.style.display='inline'; Codehighlighter1_80_117_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_80_117_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_80_117_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;text[position];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">[](std::size_t&nbsp;position)<br><img id=Codehighlighter1_158_228_Open_Image onclick="this.style.display='none'; Codehighlighter1_158_228_Open_Text.style.display='none'; Codehighlighter1_158_228_Closed_Image.style.display='inline'; Codehighlighter1_158_228_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_158_228_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_158_228_Closed_Text.style.display='none'; Codehighlighter1_158_228_Open_Image.style.display='inline'; Codehighlighter1_158_228_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span id=Codehighlighter1_158_228_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_158_228_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>const_cast</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&amp;&gt;</span><span style="COLOR: #000000">(static_cast</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;TextBlock</span><span style="COLOR: #000000">&amp;&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>(</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">)[position]);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;</span></div>
这里面有两个转型动作，而不是一个。我们打算让non-const operator[]调用const兄弟，但是non-const如果只是单纯调用operator[]，会递归调用自己。为了避免无穷递归，我们必须明确指出调用的是const operator[]。因此，这里将*this从其原始类型TextBlock&amp;转型为const TextBlock&amp;。所以这里有两次转型：第一次用来为*this添加const，第二次则是从const operator[]的返回值中移除const。添加const的那一次转型强迫进行了一次安全转型，所以采用static_cast。移除const的那个动作只能由const_cast完成，没有其他选择。<br>下面来考虑一下反向的做法：令const来调用non-const以避免重复。这个不是我们应该做的。const成员函数承诺绝对不改变其对象的逻辑状态，non-const成员函数却没有这般承诺。如果在const函数内部调用了non-const函数，就是冒了这样的风险：你曾经承诺不改动的那个对象被改动了。这就是为什么&#8220;const成员函数调用non-const成员函数&#8221;是一种错误行为：因为对象有可能因此而被改动。反向调用才是安全的：non-const函数本来就可以对其对象做任何动作，所以在其中调用一个const成员函数并不会带来任何风险。<br><br>本条目总结：<br>
<p class=docNoteTitle>Things to Remember</p>
<ul>
    <li>
    <p class=docList>Declaring something const helps compilers detect usage errors. const can be applied to objects at any scope, to function parameters and return types, and to member functions as a whole.</p>
    <li>
    <p class=docList>Compilers enforce bitwise constness, but you should program using conceptual constness.</p>
    <li>
    <p class=docList>When const and non-const member functions have essentially identical implementations, code duplication can be avoided by having the non-const version call the const version.<br></span></p>
    </li>
</ul>
<img src ="http://www.cppblog.com/xiaohe0817/aggbug/68989.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xiaohe0817/" target="_blank">Edmund</a> 2008-12-09 23:00 <a href="http://www.cppblog.com/xiaohe0817/archive/2008/12/09/68989.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>effectiv c++ 读书笔记之一</title><link>http://www.cppblog.com/xiaohe0817/archive/2008/12/08/68891.html</link><dc:creator>Edmund</dc:creator><author>Edmund</author><pubDate>Mon, 08 Dec 2008 15:50:00 GMT</pubDate><guid>http://www.cppblog.com/xiaohe0817/archive/2008/12/08/68891.html</guid><wfw:comment>http://www.cppblog.com/xiaohe0817/comments/68891.html</wfw:comment><comments>http://www.cppblog.com/xiaohe0817/archive/2008/12/08/68891.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xiaohe0817/comments/commentRss/68891.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xiaohe0817/services/trackbacks/68891.html</trackback:ping><description><![CDATA[开始写effective c++的读书笔记。今天是条款2：尽量以const，enum，inline替换#define(prefer consts,enums,and inlines to #define.)<br>现在在维护代码的时候，前辈们大片大片的宏搞得我是那个晕头转向啊，真希望他们也看过本条款<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/emsmile.gif" align=absMiddle border=0>。<br>1.Case：#define ASPECT_RATIO 1.653<br>&nbsp;&nbsp;&nbsp;Recommendation：const double AspectRatio = 1.653；<br>Reason:&nbsp;&nbsp;&nbsp;当使用ASPECT_RATIO但是获得一个编译错误信息时，可能你会很是发冏，因为这个错误信息也许会提到1.653而不是ASPECT_RATIO。如果ASPECT_RATIO定义在非你所写的头文件中，你更是因为追踪他而浪费时间。改为推荐的方式后，你找到的肯定是AspectRatio。当以常量替换#define时，有两种注意的情况，第一种是定义常量指针（const pointers）。由于常量定义式常放在头文件内，因此有必要将指针也声明为const。例如在一个头文件内定义一个常量的char*-based字符串，必须写const两次：<br>const char* const authorName = "Edmund";<br>这里采用string对象比其前辈char*-based更合适，<br>const std::string authorName("Edmund");<br>第二种是class专属常量。为了将常量的作用域限制在class内，你必须让他成为class的一个成员；而为确保此常量只有一个实体，则必须声明为static：<br>class GamePlayer{<br>private:<br>static const int NumTurns = 5;<br>int scores[NumTurns];<br>...<br>}<br>然而，你看到的是NumTurns的声明式而不是定义式，C++通常要求我们所使用的任何东西都要有一个定义式，但如果他是个class的专属常量而又是static且为整数类型（ints，chars，bools），则做特殊处理。只要不取他们的地址，你可以声明并使用他们而无需提供定义式。但如果取某个class专属常量的地址，或纵使不取地址而编译器却坚持要看到一个定义式，你就必须提供另外一个定义式：<br>const int GamePlayer::NumTurns;<br>由于NumTurns在声明时已经获得了初值，因此定义时不可以再设初值。此外，对所谓的&#8220;in-class初值设定&#8221;也只允许对整数常量进行。如果为非整型则可以采用下面的这种方式：<br>class CostEstimate{<br>private:<br>static const double FudgeFactor;<br>...<br>}<br><br>const double CostEstimate::FudgeFactor = 1.35;<br>当你在编译期需要一个class常量值，例如在上述GamePlayer::scores的数组声明中，此时如果编译器不允许&#8220;static整数型class常量&#8221;完成&#8220;in-class初值设定&#8221;，可采用enum来解决，其理论基础是&#8220;一个属于枚举类型的数值可权充ints被使用&#8221;，于是GamePlayer可定义如下：<br>class GamePlayer{<br>private:<br>enum{NumTurns = 5};<br>int scores[NumTurns];<br>...<br>};<br>注意：取一个const的值是合法的，但是取一个enum的值就是不合法的，取一个#define的值也是不合法的。如果你不想让别人获得一个pointer或者reference指向你的某个整数常量，enum可以帮助你实现这个约束。<br>下边继续说预处理器。另外一个常见的#define误用的情景是以他来实现宏，宏看起来像函数，但是不会招致函数调用带来的额外开销，例如：<br>#define CALL_WITH_MAX(a,b) f((a)&gt;(b)?(a):(b))。他的缺点就不说了，替代方式：<br>template&lt;class T&gt; inline void callWithMax(const T&amp; a, const T&amp; b)<br>{<br>f(a &gt; b?a : b);<br>}<br>本条目总结：<br>1.对于单纯常量，最好以const对象或者enums替换#defines；<br>2.对于形似函数的宏，最好改用inline函数替换#defines。<br><br><br><br><br>Ps：本文是第一次在cppblog上发表的文章，呵呵。很早就想在这上面写点了，但是不是忙这就是忙那，昨天下定决心，先把effective C++(3e)里面的55条读书笔记写在这上面。打算每天一个条目，这里面好多跟书上的句子一样，但是全是我自己敲进去的，不存在任何的paste。所写均是自己搞清楚的，不明白地方的暂时没有添加。<br><br>
<img src ="http://www.cppblog.com/xiaohe0817/aggbug/68891.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xiaohe0817/" target="_blank">Edmund</a> 2008-12-08 23:50 <a href="http://www.cppblog.com/xiaohe0817/archive/2008/12/08/68891.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>