📄 item_061.htm
字号:
"Times New Roman"'>避免在头文件中定义具有外部链接属性的实体</span><span lang=EN-US><o:p></o:p></span></i></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=SpellE><span
class=GramE><span lang=EN-US>int</span></span></span><span lang=EN-US> <span
class=SpellE>fudgeFactor</span>;</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>string</span></span><span lang=EN-US> hello("Hello,
world!");</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>void</span></span><span lang=EN-US> <span class=SpellE>foo</span>()
{<i style='mso-bidi-font-style:normal'>/* … */</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><span
lang=EN-US>symbol</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>fudgeFactor</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>hello</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>foo</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></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span lang=EN-US><span style='mso-tab-count:1'> </span><span
class=GramE>extern</span> <span class=SpellE>int</span> <span class=SpellE>fudgeFactor</span>;</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>extern</span></span><span lang=EN-US> string hello;</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span lang=EN-US>void <span
class=SpellE>foo</span>();<span
style='mso-spacerun:yes'>
</span><i style='mso-bidi-font-style:normal'>// "extern"</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><span lang=EN-US><span style='mso-tab-count:1'> </span><span
class=SpellE><span class=GramE>int</span></span> <span class=SpellE>fudgeFactor</span>;</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>string</span></span><span lang=EN-US> hello("Hello,
world!");</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>void</span></span><span lang=EN-US> <span class=SpellE>foo</span>()
{<i style='mso-bidi-font-style:normal'>/* … */</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></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal style='text-indent:36.0pt'><i style='mso-bidi-font-style:
normal'><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 style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>static</span></span><span lang=EN-US> <span class=SpellE>int</span> <span
class=SpellE>fudgeFactor</span>;</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>static</span></span><span lang=EN-US> string hello("Hello,
world!");</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>static</span></span><span lang=EN-US> void <span class=SpellE>foo</span>()
{<i style='mso-bidi-font-style:normal'>/* … */</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><span
lang=EN-US>static</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>更危险。如果是全局实体的话,至少链接器应该迅速地指出重复的定义。但是静态数据和函数的重复是合法的,因为编译器认为你在每个源文件中需要一份私有的复本。因此,如果在头文件中定义了静态数据和静态函数,而该头文件被</span><span
lang=EN-US>50</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>个文件包含,那么函数体和数据占用的空间会在最终的可执行文件中重复</span><span
lang=EN-US>50</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>fudgeFactor</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></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal style='text-indent:36.0pt'><i style='mso-bidi-font-style:
normal'><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>static</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 style='text-indent:36.0pt'><span class=GramE><span
lang=EN-US>namespace</span></span><span lang=EN-US> {</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span><span
style='mso-spacerun:yes'> </span><span class=SpellE><span
class=GramE>int</span></span> <span class=SpellE>fudgeFactor</span>;</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span><span
style='mso-spacerun:yes'> </span><span class=GramE>string</span>
hello("Hello, world!");</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span lang=EN-US><span
style='mso-spacerun:yes'> </span><span
style='mso-spacerun:yes'> </span><span class=GramE>void</span> <span
class=SpellE>foo</span>() {<i style='mso-bidi-font-style:normal'>/* … */</i>}</span></p>
<p class=MsoNormal style='text-indent:36.0pt'><span lang=EN-US>}</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><span lang=EN-US><o:p></o:p></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></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 lfo1;tab-stops:list 36.0pt'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>内联函数:虽然它们具有外部链接属性,但是链接器会保证不拒绝链接多个复本。除此之外,它们的行为和普通的函数完全一样。特别是,在一个程序中,编译器会保证一个内联函数的地址是唯一的。</span></li>
<li class=MsoNormal style='mso-list:l0 level1 lfo1;tab-stops:list 36.0pt'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>函数模板(</span><span lang=EN-US>Function templates</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>):与内联函数相似,除了重复的复本是可接受的之外(最好是完全一样的),模板实例化的行为与普通的函数一样。当然,一个好的编译系统会消除无用的复本。</span></li>
<li class=MsoNormal style='mso-list:l0 level1 lfo1;tab-stops:list 36.0pt'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>类模板的静态数据成员:这对链接器来说可能有点粗暴,不过这不是你的问题——你只需在头文件中定义它们,让编译器和链接器来处理剩余的事情。</span></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><span lang=EN-US>Schwarz
counters</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>”或“</span><span lang=EN-US>nifty
counters</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>”的全局数据初始化技术需要在头文件中植入静态(或位于匿名的名字空间中)的数据。</span><span
lang=EN-US>Jerry Schwarz</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>因为用这种技术来初始化标准</span><span
lang=EN-US>I/O</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>cin</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>cout</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>cerr</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>clog</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 + -