﻿<?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/xmli/</link><description>ぷ雪飘绛梅映残红 &lt;br&gt;
&amp;nbsp;&amp;nbsp; ぷ花舞霜飞映苍松&lt;br&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;----- Do more,suffer less</description><language>zh-cn</language><lastBuildDate>Sun, 19 Apr 2026 12:15:00 GMT</lastBuildDate><pubDate>Sun, 19 Apr 2026 12:15:00 GMT</pubDate><ttl>60</ttl><item><title>VC/MFC之ListCtrl控件使用经验总结(转)</title><link>http://www.cppblog.com/xmli/archive/2011/06/11/148482.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Sat, 11 Jun 2011 03:57:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2011/06/11/148482.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/148482.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2011/06/11/148482.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/148482.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/148482.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 以下未经说明，listctrl默认view 风格为report相关类及处理函数MFC：CListCtrl类SDK：以 &#8220;ListView_&#8221;开头的一些宏。如 ListView_InsertColumn--------------------------------------------------------------------------------1. CList...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2011/06/11/148482.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/148482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2011-06-11 11:57 <a href="http://www.cppblog.com/xmli/archive/2011/06/11/148482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c语言 printf()输出格式控制(转)</title><link>http://www.cppblog.com/xmli/archive/2011/06/09/148330.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 09 Jun 2011 03:59:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2011/06/09/148330.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/148330.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2011/06/09/148330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/148330.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/148330.html</trackback:ping><description><![CDATA[<div><span style="font-family: verdana, sans-serif; "><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 1em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; ">1．转换说明符<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %a(%A)&nbsp;&nbsp;&nbsp;&nbsp; 浮点数、十六进制数字和p-(P-)记数法(C99)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字符<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %d&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有符号十进制整数<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %f&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 浮点数(包括float和doulbe)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %e(%E)&nbsp;&nbsp;&nbsp;&nbsp; 浮点数指数输出[e-(E-)记数法]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %g(%G)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 浮点数不显无意义的零"0"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %i&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有符号十进制整数(与%d相同)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %u&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 无符号十进制整数<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %o&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 八进制整数&nbsp;&nbsp;&nbsp; e.g.&nbsp;&nbsp;&nbsp;&nbsp; 0123<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %x(%X)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 十六进制整数0f(0F)&nbsp;&nbsp; e.g.&nbsp;&nbsp; 0x1234<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %p&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指针<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字符串<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "%"</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 1em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; ">2．标志<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 左对齐："-"&nbsp;&nbsp; e.g.&nbsp;&nbsp; "%-20s"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右对齐："+" e.g.&nbsp;&nbsp; "%+20s"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 空格：若符号为正，则显示空格，负则显示"-"&nbsp;&nbsp; e.g.&nbsp;&nbsp; "% 6.2f"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #：对<span style="line-height: 21px; ">c,s,d,u类无影响；对o类，在输出时加前缀o；对x类，在输出时加前缀0x；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对e,g,f 类当结果有小数时才给出小数点。</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 1em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; ">3．格式字符串（格式）<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;［标志］［输出最少宽度］［．精度］［长度］类型<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "％-md" ：左对齐，若m比实际少时，按实际输出。<br />&nbsp;&nbsp;&nbsp;&nbsp; "%m.ns"：输出m位，取字符串(左起)n位，左补空格，当n&gt;m or m省略时m=n<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.g.&nbsp;&nbsp;&nbsp; "%7.2s" 输入CHINA<br />&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; CH"<br />&nbsp;&nbsp;&nbsp;&nbsp; "%m.nf"：输出浮点数，m为宽度，n为小数点右边数位<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.g.&nbsp;&nbsp;&nbsp; "%3.1f"&nbsp;&nbsp;&nbsp; 输入3852.99<br />&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; 输出3853.0&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 长度：为ｈ短整形量,ｌ为长整形量</p><div></div></span></div><img src ="http://www.cppblog.com/xmli/aggbug/148330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2011-06-09 11:59 <a href="http://www.cppblog.com/xmli/archive/2011/06/09/148330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Visual Studio统计有效代码行数(转)</title><link>http://www.cppblog.com/xmli/archive/2011/06/06/148157.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Mon, 06 Jun 2011 13:38:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2011/06/06/148157.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/148157.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2011/06/06/148157.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/148157.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/148157.html</trackback:ping><description><![CDATA[<div><div>看网上有人专门做了一些小工具，用来统计代码行数。感觉不是很必要。因为Visual Studio中的搜索功能支持正则表达式（虽然语法比较诡异），我们完全可以通过正则表达式来遍历整个解决方案从而获得代码行数。</div><div></div><div>^:b*[^:b#/]+.*$</div><div>需要注意：#开头和/开头或者空行都不计入代码量。</div><div></div><div>如果需要只统计代码文件的代码量，可以按住Ctrl+Shift+F之后选择查找文件的类型。</div></div><img src ="http://www.cppblog.com/xmli/aggbug/148157.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2011-06-06 21:38 <a href="http://www.cppblog.com/xmli/archive/2011/06/06/148157.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>std::string is contiguous (转)</title><link>http://www.cppblog.com/xmli/archive/2011/05/26/147293.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 26 May 2011 12:52:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2011/05/26/147293.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/147293.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2011/05/26/147293.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/147293.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/147293.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文地址：&nbsp;http://hpyblg.wordpress.com/2010/06/03/stdstring-is-contiguous/You can safely assume that the memory buffer used by std::string is contiguous. Specifically, the address of the string&#8217;...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2011/05/26/147293.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/147293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2011-05-26 20:52 <a href="http://www.cppblog.com/xmli/archive/2011/05/26/147293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏客户端与编辑器代码重用设计杂谈</title><link>http://www.cppblog.com/xmli/archive/2010/12/10/136043.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 10 Dec 2010 04:28:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/12/10/136043.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/136043.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/12/10/136043.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/136043.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/136043.html</trackback:ping><description><![CDATA[<p align="CENTER" style="margin-bottom: 0cm"><font color="#000000"><font size="4" style="font-size: 16pt"><strong><span lang="zh-CN">游戏客户端与编辑器</span>代码重<span lang="zh-CN">用</span>设计<span lang="zh-CN">杂谈</span></strong></font></font></p>
<p align="CENTER" style="margin-bottom: 0cm">版本：<font face="Times New Roman, serif">0.1</font></p>
<p align="CENTER" style="margin-bottom: 0cm">最后修改：<font face="Times New Roman, serif">2010-12-10</font></p>
<p align="CENTER" style="margin-bottom: 0cm">撰写：李现民</p>
<p align="CENTER" style="margin-bottom: 0cm"><br>
</p>
<h1 lang="zh-CN" class="cjk">引言</h1>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>很多游戏都有配套的编辑器，或通用或专用，这样可以方便策划及时设计、修改游戏数据。当一个游戏方案确认实施时，如果需要设计配套编辑器，那么它往往先于游戏本身而设计。出于代码重用和方便维护的需要，大部分核心代码会在游戏客户端与编辑器中同时使用，因此有效提取这部分共用代码并尽量减少与项目其它部分的耦合就成为设计的重点。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>关于良好程序架构设计的话题，比如设计模式、领域驱动设计等，相关论著恒河沙数。本文结合实践中遇到的问题，从工具与技术相结合的角度来阐述相关问题的解决方案。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>以下假定程序运行环境为<font face="Times New Roman, serif">VC6+XP</font>。从本文撰写时间看（<font face="Times New Roman, serif">2010-12-10</font>），<font face="Times New Roman, serif">VC6</font>无论如何都不是一个好的选择，但限于笔者所在公司环境如此，所以只好将就着来了╮<font face="Times New Roman, serif">(╯▽╰)╭</font>。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<h1 class="cjk">使用宏控制代码生成策略</h1>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>尽管我们追求代码的可重用性，但实际情况往往并不尽如人意，特别是在与特定于游戏客户端<span lang="zh-CN">（</span>或编辑器<span lang="zh-CN">）</span>的功能相结合比较紧密的<span lang="zh-CN">代码</span>部分<span lang="zh-CN">。</span>比如<font face="Times New Roman, serif">UI</font>（<span lang="zh-CN">界面</span>），游戏<span lang="zh-CN">客户端</span>中有独立的界面模块，而编辑器界面<span lang="zh-CN">可能</span>使用<font face="Times New Roman, serif">MFC</font>制作<span lang="zh-CN">。即使同一个函数接口，游戏客户端与编辑器所需要的功能也可能是不一样的，这是因为它们拥有各自不同的应用倾向：游戏客户端倾向于使游戏画质更加平滑，而编辑器则需要考虑策划人员快速的编辑修改数据；再比如游戏客户端可能需要网络</span><font face="Times New Roman, serif">IO</font><span lang="zh-CN">功能，而编辑器则一般不需要。</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">宏（具体的说，</span><font face="Times New Roman, serif">C++</font><span lang="zh-CN">中的宏），此时可能是一种比较合适的工具。比如，通过在游戏客户端与编辑器中定义不同的宏变量，可以使游戏客户端专用的网络</span><font face="Times New Roman, serif">IO</font><span lang="zh-CN">代码在编辑器中根本不生成。</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>某些情况下可能需要在<font color="#800000">同一个项目下建立多个<font face="Times New Roman, serif">configurations</font></font><font color="#800000">（配置），通过定义不同的宏变量以控制生成不同版本的程序，比如：简化版、完整版、内部版等</font>。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>宏在<font face="Times New Roman, serif">VC</font>环境中有大量的应用案例，比如<font face="Times New Roman, serif">windows.h</font>头文件中定义了大量的宏用于控制不同环境下的代码生成策略。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<h1 class="cjk"><span lang="zh-CN">使用函数控制代码生成策略并信任</span>编译器<span lang="zh-CN">优化</span></h1>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">宏控制的原理是将不需要的代码当作注释直接移除，因此编</span><font color="#800000"><span lang="zh-CN">译器不会去审查该部分代码的正确性。这在某些情况下是必须的</span></font><span lang="zh-CN">，比如编辑器没有网络</span><font face="Times New Roman, serif">IO</font><span lang="zh-CN">相关的代码接口，因此相关代码必须被清除，否则编辑器项目将无法正确编译。</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>但宏控制有自己的问题：</p>
<ol>
	<li><p lang="zh-CN" style="margin-bottom: 0cm">宏变量通常定义在<font face="Times New Roman, serif">Project
	Settings</font>（工程设置）中，因此不容易记忆或查找；</p>
	</li><li><p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">IDE</font>工具通常无法像支持代码一样支持此类宏变量的快速查找，特别是存在多个项目相互引用的复杂工程中（比如<font face="Times New Roman, serif">Visual
	Assist X</font>有<font face="Times New Roman, serif">Find
	Reference</font>功能，可以快速搜索到所有引用指定变量或函数的代码，但此功能不支持在<font face="Times New Roman, serif">Project
	Settings</font>中定义的宏变量）；</p>
	</li><li><p lang="zh-CN" style="margin-bottom: 0cm">编译器无法审查被移除部分代码的正确性，这可能导致一些代码修改同步的问题。</p></li></ol>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>针对这些问题，笔者的解决方案是：宏控制变量只使用一次，用于定义一个简单函数，而该函数返回当前宏控制变量的存在情况，其它原本使用宏控制变量的地方都改为使用这个函数判断。这样<font color="#800000">间接的将宏变量控制转换为函数控制，从而获得<font face="Times New Roman, serif">IDE</font></font><font color="#800000">工具支持与编译器代码审查的双重好处</font>。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>比如如下代码：</p>
<p style="margin-bottom: 0cm; background: #e6e6e6"><font face="Times New Roman, serif"><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">namespace</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">edition<br></span></font></font></font><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">{<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">#ifdef</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">_EDITOR<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">inline</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">bool</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">IsEditor</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()
{ </span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">return</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">true</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;
}<br></span></font></font></span><span  style="color: rgb(0, 0, 255); font-family: 'Times New Roman', serif; font-size: 14.4px; ">#else<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">inline</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">bool</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">IsEditor</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()
{ </span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">return</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">false</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;
}<br></span></font></font></span><span  style="color: rgb(0, 0, 255); font-family: 'Times New Roman', serif; font-size: 14.4px; ">#endif<br></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">}<br><span  style="font-size: medium; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx"><font  color="#000000"><br></font>void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Print</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()<br></span></font></font></span></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">{<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">if</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">edition</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">IsEditor</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">())<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">{<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">puts</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">"This
is editor"</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">}<br></span><span  style="color: rgb(0, 0, 255); font-family: 'Times New Roman', serif; font-size: 14.4px; ">else<br></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">{<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">puts</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">"This
is not editor"</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">}<br></span><span  style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">}</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>宏变量<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">_EDITOR</span></font></font></font>只使用一次，其余地方都使用<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">edition</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">IsEditor</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()</span></font></font></font>区分是编辑器代码还是游戏客户端代码。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>请注意，我们并不会有任何的运行期性能损失，虽然看起来并非如此。由于在编译期<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">edition</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">IsEditor</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()</span></font></font></font>的值是确定的，因此<font color="#800000">当打开优化时编译器会移除不可达代码，从而得到与宏控制情况下相同的可执行文件</font>。当然，在<font face="Times New Roman, serif">Debug</font>版本下（优化关闭）所有的代码都被编译生成到最终可执行文件中，但我猜您应该不会将<font face="Times New Roman, serif">Debug</font>版本给最终用户使用对吧？</p>
<p style="margin-bottom: 0cm"><br>
</p>
<h1 class="cjk">使用<font face="Times New Roman, serif">delegate</font>解耦</h1>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">在</span><font face="Times New Roman, serif">MVC</font><span lang="zh-CN">架构下，使用</span><font face="Times New Roman, serif">Observer</font><span lang="zh-CN">（观察者）模式将核心逻辑代码与</span><font face="Times New Roman, serif">UI</font><span lang="zh-CN">界面代码分离似乎天经地义的事，这样做的好处是核心逻辑代码可以独立于</span><font face="Times New Roman, serif">UI</font><span lang="zh-CN">代码而存在，从而达到重用的目的。但不幸的是，从笔者经手的代码看，很多程序员并没有注意到这一点。主要问题可能包括以下两个方面：</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>第一是核心逻辑代码与<font face="Times New Roman, serif">UI</font>界面代码相互调用关系错综复杂。由于核心逻辑代码不独立，因而很难进行提取复用。这种情况相对比较常见。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>第二个问题解释起来可能更复杂一些。由于缺乏从核心逻辑代码到<font face="Times New Roman, serif">UI</font>界面代码的回调机制，程序员可能会被迫使用一些极端的手法来达到侦测指定事件是否发生的目的。比如，我们知道游戏客户端都有一个主循环<font face="Times New Roman, serif">main_loop</font>，方法名称通常叫<font face="Times New Roman, serif">Update()</font>或<font face="Times New Roman, serif">Tick()</font>，用于更新每一帧的游戏动画。这时，程序员可能会在该循环中埋伏一些代码以侦测核心逻辑状态的变化情况，从而达到触发事件的目的。这种手法实现了功能，保持了低耦合，却降低了代码执行效率。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这两个问题的解决之道在于观察者模式。这个模式在实现上还是比较复杂的，对每一个要处理事件都需要定义对应的观察者与被观察者接口。这种代码复杂性曾使很多人望而却步（包括本人<font face="Times New Roman, serif">-___-</font>），为此<font face="Times New Roman, serif">java</font>中内置了<font face="Times New Roman, serif">java.util.Observer</font>与<font face="Times New Roman, serif">java.util.Observable</font>接口，以降低使用该模式的代价。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">笔者建议的方案是使用</span><font face="Times New Roman, serif">delegate</font><span lang="zh-CN">（委托）。没错，就是那个</span><font face="Times New Roman, serif">C#</font><span lang="zh-CN">中的</span><font face="Times New Roman, serif">delegate</font><span lang="zh-CN">，它能够极低的设计复杂度实现与观察者模式相同的解耦效果。具体实例这里不再列举，因为网上可以找到很多。如果你使用的是</span><font face="Times New Roman, serif">C#</font><span lang="zh-CN">，那么你是幸运的；如果你使用的是</span><font face="Times New Roman, serif">C++</font><span lang="zh-CN">，那么网上同样可以找到设计好的仿真类库；如果你不幸使用了</span><font face="Times New Roman, serif">VC6</font><span lang="zh-CN">，并且实在找到出路了，那么同学你也许可以去参考一下我的另一篇文章《</span><font face="Times New Roman, serif">VC6</font><span lang="zh-CN">中简易</span><font face="Times New Roman, serif">delegate</font><span lang="zh-CN">实现》，或许会有点帮助。</span><font face="Times New Roman, serif">	</font></p>
<p style="margin-bottom: 0cm"><br>
</p>
<h1 class="cjk">结语</h1>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">本来还想加点静态变量与通用工厂的话题的，但我发现</span><font face="Times New Roman, serif">meyers
singleton</font><span lang="zh-CN">在</span><font face="Times New Roman, serif">VC6</font><span lang="zh-CN">中的某种应用模式下会问题（</span><font face="Times New Roman, serif">singleton</font><span lang="zh-CN">对象的构造函数会被调用两次，</span><font face="Times New Roman, serif">T__T</font><span lang="zh-CN">），因此先欠着账，等待下次有成熟方案的时候再说吧。不过对此问题诸位看官如果有相关宝贵经验的话不妨提携一二，感激不尽中。</span></p>
<img src ="http://www.cppblog.com/xmli/aggbug/136043.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-12-10 12:28 <a href="http://www.cppblog.com/xmli/archive/2010/12/10/136043.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++多态技术（转）</title><link>http://www.cppblog.com/xmli/archive/2010/12/07/135666.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Tue, 07 Dec 2010 02:52:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/12/07/135666.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/135666.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/12/07/135666.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/135666.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/135666.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 作者：荣耀提交者：eastvc 发布日期：2003-12-14 19:38:12原文出处：http://www.royaloo.com/articles/articles_2003/PolymorphismInCpp_content.htm摘要本文描述了C++中的各种多态性。重点阐述了面向对象的动态多态和基于模板的静态多态，并初步探讨了两种技术的结合使用。&nbsp;关键词多态&nbsp; 继承&...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2010/12/07/135666.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/135666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-12-07 10:52 <a href="http://www.cppblog.com/xmli/archive/2010/12/07/135666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>冒泡和选择排序该被踢出教材了(转)</title><link>http://www.cppblog.com/xmli/archive/2010/11/25/134594.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 25 Nov 2010 01:59:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/11/25/134594.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/134594.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/11/25/134594.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/134594.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/134594.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 文章源于csdn的一篇帖子，原地址：http://topic.csdn.net/u/20100805/20/231B9356-847D-4FE9-87BA-2A2A3C55CA76.html非常汗颜，楼主所提到的一些方法，像鸽巢排序与梳排序的名字我都没听说过，故摘抄至此，共勉之！------------------------------------------------------------...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2010/11/25/134594.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/134594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-11-25 09:59 <a href="http://www.cppblog.com/xmli/archive/2010/11/25/134594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于常量折叠(转)</title><link>http://www.cppblog.com/xmli/archive/2010/11/23/134425.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Tue, 23 Nov 2010 13:24:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/11/23/134425.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/134425.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/11/23/134425.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/134425.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/134425.html</trackback:ping><description><![CDATA[
<span style="color: rgb(48, 48, 48); font-family: Verdana, Helvetica, Arial; line-height: 21px; font-size: 11.6667px; "><p>首先来看一个例子：</p><p>int main(int argc, char* argv[])<br>{<br>const int i=0;<br>int *j = (int *) &amp;i;<br>*j=1;<br>cout&lt;&lt;&amp;i&lt;&lt;endlcout&lt;&lt;j&lt;&lt;endl;<br>cout&lt;&lt;i&lt;&lt;endl;<br>cout&lt;&lt;*j&lt;&lt;endl;<br>return 0;<br>}</p><p>结果是</p><p>0012ff7c<br>0012ff7c</p><p>0</p><p>1</p><p>因为i和j都指向相同的内存地址，所以输出的前两个结果是相同的，但为啥相同的内存里的结果不相同么？－－这就是常量折叠.</p><p>这个"常量折叠"是 就是在编译器进行语法分析的时候，将常量表达式计算求值，并用求得的值来替换表达式，放入常量表。可以算作一种编译优化。<br>我只是改了这个地址内容,但是i还是0,</p><p>因为编译器在优化的过程中，会把碰见的const全部以内容替换掉（跟宏似的: #define pi 3.1415,用到pi时就用3.1415代替），这个出现在预编译阶段；但是在运行阶段，它的内存里存的东西确实改变了!!!</p><p>6.网上的一些问题（4）</p><br><p>关于常量</p><p>这些天被常量的一些概念折磨着,现在终于有些明白了，</p><p>问题始于const int i = 10;//i存在哪，10存在哪</p><p>说明一：符号表</p><p>这个语句是对i的声明，因为编译器一开始就知道i的值，所以以后出现i时就会用10代替，这好像叫做符号表的概念，i就对应10了。</p><p>网上一篇帖子上有这样的代码：</p><p>const int a = 3;</p><p>int *p = const_cast&lt;int *&gt;(&amp;a);</p><p>*p = 4;</p><p>cout &lt;&lt; a;//仍然输出3</p><p>这个结果可以用上面的说明来解释</p><p>说明二：常量折叠（const folding）与复写传播 (copy propagation)</p><p>网上人们普遍反映thinking in c++将const folding译为常量折叠是种误导，我觉得译的还行，本来folding就有折叠的意思，就是把原来的东西变小，而象const int i = 2*2;编译器确实将2*2算成4了，以后碰到i就用4替换，这个计算2*2的过程据说叫常量折叠--const folding，而用4替换i的过程叫做复写传播--copy propagation.他们都是编译器的优化技术</p><br><p>说明三：为常量分配空间</p><p>补充一下，这里说的都是const 定义的常量，而非文字常量，</p><p>（c++ primer翻译成文字常量--literal constant</p><p>the c++ programming language（tcpl）翻译成文字量，还分了不同类型）</p><p>至于文字常量存在哪，c++ primer 3ed上说它们是不可寻址的--nonaddressable，尽管它们也存在机器内存某个地方，但无法访问它们的地址</p><p>对于int double等类型还好理解，但是对于字符串常量（tcpl里说将字符串文字量作为常量，利于存储与访问时的优化）下面的代码似乎表示字符串常量存储在静态存储区里（字符串文字量是静态分配的--tcpl），那么字符串常量的地址不是可以访问了吗，在静态存储区里</p><p><a href="http://bbs.bc-cn.net/dispbbs.asp?boardid=56&amp;replyi" style="color: rgb(54, 105, 0); text-decoration: none; ">http://bbs.bc-cn.net/dispbbs.asp?boardid=56&amp;replyi</a>...</p><p>字符串文字量的类型是常量字符数组--适当个数的const字符的数组</p><p>//有关字符常量的存储区的问题</p><p>//另外，char a[]和char *a的区别</p><p>//"hello world 1"存在哪<br>#include &lt;iostream&gt;<br>using namespace std;<br>int main()<br>{</p><p><br>char* p = "hello world1"；<br>char a[] = "hello world2"；<br>//会为a在栈上分配13个字节的空间<br>// p[2] = a;<br>a[2] = a;<br>char* p1 = "hello world1"<br>printf("%xn",&amp;p[2]);//p应该指向常量区<br>printf("%x",&amp;a[2]);//栈上数组第三个元素的地址<br>return 0;<br>//结果42f036 //这是常量区<br>//12ff6e果然不一样，这是栈区<br>}</p><br><p>6.总结</p><p>那么"常量折叠"到底是啥意思呢？</p><p>我理解，简单的说就是，当编译器处理const的时候，编译器会将其变成一个立即数。</p><p>《thinking in c++》里面说这一点在数组定义里尤其重要(为啥呢？没有查到相关的资料)。</p><p class="zoundry_bw_tags"><span class="ztags"><span class="ztagspace">Technorati</span>&nbsp;:&nbsp;<a href="http://technorati.com/tag/Zoundary" class="ztag" rel="tag" style="color: rgb(54, 105, 0); text-decoration: none; ">Zoundary</a>,&nbsp;<a href="http://technorati.com/tag/test" class="ztag" rel="tag" style="color: rgb(54, 105, 0); text-decoration: none; ">test</a>,&nbsp;<a href="http://technorati.com/tag/%E5%B8%B8%E9%87%8F%E6%8A%98%E5%8F%A0" class="ztag" rel="tag" style="color: rgb(54, 105, 0); text-decoration: none; ">常量折叠</a></span></p></span><img src ="http://www.cppblog.com/xmli/aggbug/134425.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-11-23 21:24 <a href="http://www.cppblog.com/xmli/archive/2010/11/23/134425.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>const灵异现象</title><link>http://www.cppblog.com/xmli/archive/2010/11/22/134275.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Mon, 22 Nov 2010 07:16:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/11/22/134275.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/134275.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/11/22/134275.html#Feedback</comments><slash:comments>14</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/134275.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/134275.html</trackback:ping><description><![CDATA[

<p align="CENTER" style="margin-bottom: 0cm"><font color="#000000"><font face="Times New Roman, serif"><font size="4" style="font-size: 16pt"><strong>const</strong></font></font><font size="4" style="font-size: 16pt"><strong>灵异现象</strong></font></font></p>
<p align="CENTER" style="margin-bottom: 0cm">版本：<font face="Times New Roman, serif">0.1</font></p>
<p align="CENTER" style="margin-bottom: 0cm">最后修改：<font face="Times New Roman, serif">2010-11-22</font></p>
<p align="CENTER" style="margin-bottom: 0cm">撰写：李现民</p>
<p lang="zh-CN" align="CENTER" style="margin-bottom: 0cm"><br>
</p>
<h1 class="cjk">概述</h1>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	const</font><span lang="zh-CN">在</span><font face="Times New Roman, serif">c++</font><span lang="zh-CN">中意味着&#8220;不可改变&#8221;，但在有些情况下我们可以&#8220;合法&#8221;地绕过编译器去修改一些</span><font face="Times New Roman, serif">const</font><span lang="zh-CN">数据，比如</span><font face="Times New Roman, serif">const_cast</font><span lang="zh-CN">就可以剥离一个对象的</span><font face="Times New Roman, serif">const</font><span lang="zh-CN">属性。然而，我们这样做在多大程度上是&#8220;合理&#8221;的，却因不同的问题而论，也许一不小心，你就可能掉入陷阱之中。</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>以下问题，我只分析，不说话，请各位看官自己判断。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<h2 class="cjk">当<span lang="zh-CN">目标</span>是一个常数</h2>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这件事源于在网上看到的一篇文章，其来源已经不可考，但大意是：就如下<font face="Times New Roman, serif">C++</font>程序，其输出是什么：</p>
<p align="LEFT" style="margin-bottom: 0cm; background: #e6e6e6"><font face="Times New Roman, serif"><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">foo</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">const</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">int</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">a</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">	=
1;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">int</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">		=
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">const_cast</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">int</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*&gt;(&amp;</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">a</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">		=
2;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">printf</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">"
a=	%d\n *p=	%d\n &amp;a=	%x\n p=	%x \n\n"</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">,
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">a</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">,
*</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">,
&amp;</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">a</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">,
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">}</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>我在<font face="Times New Roman, serif">VC2008</font>下的实测结果为：</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm; background: #e6e6e6">
 <font face="Times New Roman, serif">a	=    1<br></font><span style="font-family: 'Times New Roman', serif; ">*p	=    2<br></span><span style="font-family: 'Times New Roman', serif; ">&amp;a	=    12ff6c<br></span><span style="font-family: 'Times New Roman', serif; ">p	=    12ff6c</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">好了，问题出现：</span><font color="#800000"><span lang="zh-CN">明明</span><font face="Times New Roman, serif">p</font></font><font color="#800000"><span lang="zh-CN">所指向的就是变量</span><font face="Times New Roman, serif">a</font></font><font color="#800000"><span lang="zh-CN">，但为何打印其值时</span><font face="Times New Roman, serif">a!=*p</font></font><span lang="zh-CN">？</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这并非是我用错了<font face="Times New Roman, serif">const_cast</font>，也不是编译器进行了优化的问题。事实上，在各版本的<font face="Times New Roman, serif">VC</font>与<font face="Times New Roman, serif">g++</font>下的运行结果均是如此。</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>以下是<font face="Times New Roman, serif">VC2008</font>下<font face="Times New Roman, serif">debug</font>版本的汇编代码：</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm; background: #e6e6e6">
<font face="Times New Roman, serif"><font size="2" style="font-size: 9pt"><span lang="zxx">	const
int a	= 1;<br></span></font></font><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041146E
 mov         dword ptr [a],1<br></span><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">int*
p		= const_cast&lt;int*&gt;(&amp;a);<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411475
 lea           eax,[a]<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411478
 mov         dword ptr [p],eax<br></span><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">*p			=
2;<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041147B
 mov         eax,dword ptr [p]<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041147E
 mov         dword ptr [eax],2<br></span><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">printf("
a=	%d\n *p=	%d\n &amp;a=	%x\n p=	%x \n\n", a, *p, &amp;a, p);<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411484
 mov         esi,esp<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411486
 mov         eax,dword ptr [p]<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411489
 push        eax<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041148A
 lea         ecx,[a]<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041148D
 push        ecx<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041148E
 mov         edx,dword ptr [p]<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411491
 mov         eax,dword ptr [edx]<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411493
 push        eax<br></span><span style="color: rgb(128, 0, 0); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411494
 push        1<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">00411496
 push        offset string " a=\t%d\n *p=\t%d\n &amp;a=\t%x\n
p=\t%x \n\n"... (415808h)<br></span><span style="color: rgb(128, 128, 128); font-family: 'Times New Roman', serif; font-size: 14.4px; ">0041149B
 call        dword ptr [__imp__printf (419318h)]</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">从</span><font face="Times New Roman, serif">printf()</font><span lang="zh-CN">的四个参数入栈过程中我们可以看出：指针</span><font face="Times New Roman, serif">p</font><span lang="zh-CN">的确指向变量</span><font face="Times New Roman, serif">a</font><span lang="zh-CN">了，而变量</span><font face="Times New Roman, serif">a</font><span lang="zh-CN">处的数值也的确被改写成</span><font face="Times New Roman, serif">2</font><span lang="zh-CN">了，</span><font color="#800000"><span lang="zh-CN">问题是：当压入</span><font face="Times New Roman, serif">a</font></font><font color="#800000"><span lang="zh-CN">的值的时候，编译器直接压入了其原始数值</span><font face="Times New Roman, serif">1</font></font><span lang="zh-CN">。</span></p>
<p align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">关键其实在于：</span><font face="Times New Roman, serif"><font color="#800000">const_cast</font></font><font color="#800000"><span lang="zh-CN">所操作的目标是否为基础数据类型（</span><font face="Times New Roman, serif">char,
int, float, double</font></font><font color="#800000"><span lang="zh-CN">等），如果是类（或结构体）对象则又将是另一番情形</span></font><span lang="zh-CN">。</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<h2 class="cjk">当修改字符串常量</h2>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">这个问题最早见于一篇文章《</span><font face="Times New Roman, serif">Solmyr</font><span lang="zh-CN">的小品文系列之一：字符串放在哪里？》，在这里我只不过转述一二。</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>代码如下：</p>
<p style="margin-bottom: 0cm; background: #e6e6e6"><font face="Times New Roman, serif">	<font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">foo</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">char</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">str1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">			=
</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">"watch"</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">const</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">char</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">str2</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">		=
</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">"watch"</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">char</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">str3</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">[]			=
</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">"watch"</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br><span style="font-size: medium; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx"><br>str1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">[0]			=
</span></font></font><font color="#a31515"><font size="2" style="font-size: 9pt"><span lang="zxx">'m'</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br><span style="font-size: medium; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx"><font color="#000000"><br></font>std</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">cout</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;&lt;
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">str1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&lt;&lt; </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">endl</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&lt;&lt; </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">str2</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&lt;&lt; </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">endl</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&lt;&lt; </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">str3</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&lt;&lt; </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">endl</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span></span></font></font></span></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 14.4px; ">}</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	VC6</font>中<font face="Times New Roman, serif">Release</font>版本运行结果如下：</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm; background: #e6e6e6">
<font face="Times New Roman, serif">match<br></font><span style="font-family: 'Times New Roman', serif; ">match<br></span><span style="font-family: 'Times New Roman', serif; ">watch</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	VC2008</font>中<font face="Times New Roman, serif">Release</font>版本运行结果如下：</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm; background: #e6e6e6">
<font face="Times New Roman, serif">watch<br></font><span style="font-family: 'Times New Roman', serif; ">watch<br></span><span style="font-family: 'Times New Roman', serif; ">watch</span></p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>容易看出：这段代码的运行结果决定于编译器，因为我们改写了不应该被改写的常量数据。更根本的原因是：由于编译器优化，<font face="Times New Roman, serif"><font color="#800000">str1</font></font><font color="#800000">与<font face="Times New Roman, serif">str2</font></font><font color="#800000">实际上指向的是同一份&#8221;<font face="Times New Roman, serif">watch&#8221;</font></font><font color="#800000">字符串</font>。</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这还带出了另一件事：<font color="#800000">尽管<font face="Times New Roman, serif">str1</font></font><font color="#800000">的声明中不带<font face="Times New Roman, serif">const</font></font><font color="#800000">，但它所指向的字符串数据隐含是<font face="Times New Roman, serif">const</font></font><font color="#800000">类型的</font>。</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>注意：<font color="#800000">这段代码只有<font face="Times New Roman, serif">Release</font></font><font color="#800000">版本才能顺利执行，<font face="Times New Roman, serif">Debug</font></font><font color="#800000">版版本运行时会得到一个<font face="Times New Roman, serif">Access
violation </font></font>。</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><br></p><img src ="http://www.cppblog.com/xmli/aggbug/134275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-11-22 15:16 <a href="http://www.cppblog.com/xmli/archive/2010/11/22/134275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++通用删除器设计</title><link>http://www.cppblog.com/xmli/archive/2010/11/15/133653.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Mon, 15 Nov 2010 06:46:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/11/15/133653.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/133653.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/11/15/133653.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/133653.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/133653.html</trackback:ping><description><![CDATA[



<p align="CENTER" style="margin-bottom: 0cm"><font color="#000000"><font face="Times New Roman, serif"><font size="4" style="font-size: 16pt"><strong>C++</strong></font></font><font size="4" style="font-size: 16pt"><strong>通用删除器设计</strong></font></font></p>
<p align="CENTER" style="margin-bottom: 0cm">版本：<font face="Times New Roman, serif">0.1</font></p>
<p align="CENTER" style="margin-bottom: 0cm">最后修改：<font face="Times New Roman, serif">2010-11-15</font></p>
<p align="CENTER" style="margin-bottom: 0cm">撰写：李现民</p>
<p lang="zh-CN" align="CENTER" style="margin-bottom: 0cm"><br>
</p>
<h1 class="cjk">概述</h1>
<p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>很久以前，我写过一篇短文讨论如何在<font face="Times New Roman, serif">C++</font>项目中避免使用<font face="Times New Roman, serif">delete</font>的设想，基本方法是使用域（<font face="Times New Roman, serif">scope</font>）对象或<font face="Times New Roman, serif">std::auto_ptr</font>代替。尽管当时已经讨论在所有可能的情况，但后面在实际项目实施中发现效果并不好。原因是方面的，比如在使用<font face="Times New Roman, serif">std::auto_ptr</font>时会存在以下不得因素：</p>
<ol>
	<li><p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm">可能的额外开销外（其实很小）；</p>
	</li><li><p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm">你需要时刻小心对象所有权的问题。尽管可能只需要稍微注意一下就可以了，但似乎没有任何程序员喜欢过提心吊胆的日子；</p>
	</li><li><p lang="zh-CN" align="LEFT" style="margin-bottom: 0cm">你不能在容器（比如<font face="Times New Roman, serif">std::vector</font>）中存储<font face="Times New Roman, serif">std::auto_ptr</font>对象；</p>
</li></ol>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">基于以上原因，类似于</span><font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pText
</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">=
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">new</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;</span></font></font></font><span lang="zh-CN">这种直接在堆上申请内存的方式还是在代码得到了大量应用。而接下来就是如何安全、有效的回收这些内存的问题，这也正是本文所讨论的话题。</span></p>
<h2 class="cjk">回收单个堆对象</h2>
<p lang="zh-CN" style="margin-bottom: 0cm; background: #e6e6e6"><font color="#008000"><font face="Times New Roman, serif"><font size="2" style="font-size: 9pt"><span lang="zxx">//
delete a object pointer and reset it<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">template</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">class</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">T</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&gt;
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">T</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*&amp;
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">{<br></span><span  style="color: rgb(0, 128, 0); font-family: 'Times New Roman', serif; font-size: 12px; ">//
check if T is incomplete type, if it is, the compiler will report an
error<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">typedef</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">char</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">type_must_be_complete</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">[
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">sizeof</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">T</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)?
1: -1 ];<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; "><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">sizeof</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">type_must_be_complete</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span  style="color: rgb(0, 128, 0); font-family: 'Times New Roman', serif; font-size: 12px; ">//
delete the pointer and reset it<br></span><span  style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">delete</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
=</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">NULL</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span  style="font-family: 'Times New Roman', serif; font-size: 12px; ">}</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这是一个模板函数，它主要有三个作用：</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>第一个作用是检查被删除对象的类型完整性。这通常无法引起人们的重视，但在某些情况下可能会导致未定义行为，比如以下代码：</p>
<p style="margin-bottom: 0cm; background: #e6e6e6"><font face="Times New Roman, serif"><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">	</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pText</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">	=
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">new</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pData</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">	=
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pText</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">delete</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pData</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;</span></font></font></span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	<font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*</span></font></font></font>类对象<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pText
</span></font></font></font>被转换成了拥有<font face="Times New Roman, serif"><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*</span></font></font></font>对象<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pData</span></font></font></font>，并对<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pData
</span></font></font></font>调用了<font face="Times New Roman, serif"><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">delete
</span></font></font></font>删除操作。在这种情况下编译器的行为是未知的，但至少有一点：由于编译器无法推导<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">pData
</span></font></font></font>的原始类型，因此无法调用对象的析构函数。</p>
<p lang="zh-CN" style="margin-bottom: 0cm; background: #e6e6e6"><font face="Times New Roman, serif">	<font color="#008000"><font size="2" style="font-size: 9pt"><span lang="zxx">//
check if T is incomplete type, if it is, the compiler will report an
error<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">typedef</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">char</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">type_must_be_complete</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">[
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">sizeof</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">T</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)?
1: -1 ];<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">sizeof</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">type_must_be_complete</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);</span></font></font></span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这两句代码可以检查被删除对象的类型完整性。其效果发生在编译期，如果对类型不完整的对象调用<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null
</span></font></font></font>删除操作，将引起编译错误。它<font color="#800000">没有运行期开销</font>，因此使用<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null
</span></font></font></font>带来的安全性实际上免费的。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>更加详细的解释可以参考<font face="Times New Roman, serif">boost</font>库中的<font face="Times New Roman, serif">checked_delete.hpp</font>。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	<font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null
</span></font></font></font>的第二个作用是回收堆对象，这没有什么可说的。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	<font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null
</span></font></font></font>的第三个作用是将对象指针设置为<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">NULL</span></font></font></font><font color="#010001"><font size="2" style="font-size: 9pt">，</font></font>这主要是为了应对<font color="#800000">指针有效性检查</font>，属于常规手段。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>另外，注意到<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null
</span></font></font></font>被设计为一个模板函数，在发布版本（<font face="Times New Roman, serif">Release</font>）中，它将以内联代码（<font face="Times New Roman, serif">inline</font>）的形式存在，因此<font color="#800000">不会有运用期函数调用开销</font>。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<h2 class="cjk">回收容器中的堆对象</h2>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm; background: #e6e6e6"><font color="#008000"><font face="Times New Roman, serif"><font size="2" style="font-size: 9pt"><span lang="zxx">//
delete container (std::vector, std::list) items and reset them to
NULL<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">template</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">typename</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">InputIterator</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&gt; </span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">InputIterator</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">first</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">,
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">InputIterator</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">last</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">while</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">last</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
!= </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">first</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(*</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">first</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">++</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">first</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">}<br></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">}<br><span style="color: rgb(0, 128, 0); "><font color="#000000"><br></font>//
delete functor, used for iterative delete<br></span></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">struct</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">deleter<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">template</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">typename</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">T</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
&gt; </span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">void</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">operator</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">()(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">T</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*&amp;
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">p</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">}<br></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">};</span></p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>这段代码分为两部分：一个同样叫<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null
</span></font></font></font>的模板函数与一个名为<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">deleter</span></font></font></font>的仿函数。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>先来看第一部分，它同样叫<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null</span></font></font></font>，与前面介绍的那个版本所不同的是它接受一对迭代器作用输入条件，其作用是回收<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">[first,
last)
</span></font></font></font>范围内所有堆对象。与<font face="Times New Roman, serif">std::for_each</font>等很多<font face="Times New Roman, serif">STL</font>标准算法类似，该函数可以同时应用于<font color="#800000">普通数组或存储单值的标准容器</font>（包括<font face="Times New Roman, serif">std::vector,
std::list, std::set</font>等，不包含<font face="Times New Roman, serif">std::map</font>）。</p>
<p lang="zh-CN" style="margin-bottom: 0cm"><br>
</p>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font>第二部分比较有意思：它是一个仿函数。它可以在一定程度上代替<font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null(first,
last)</span></font></font></font>，<span lang="zh-CN">以下代码展示了分别使用这两种方式回收容器中的堆对象的方法</span>：</p>
<p style="margin-bottom: 0cm; background: #e6e6e6"><font face="Times New Roman, serif"><font size="2" style="font-size: 9pt"><span lang="zxx">	</span></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">typedef</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">vector</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">*&gt;
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">TextPack</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">TextPack</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">,
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts2</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">const</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">int</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">datasize</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">	=
100;<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">for</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
(</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">int</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">i</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">=
0; </span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">i</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">&lt;
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">datasize</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">;
++</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">i</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">)<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">{<br></span><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">.</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">push_back</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">new</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts2</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">.</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">push_back</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#0000ff"><font size="2" style="font-size: 9pt"><span lang="zxx">new</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">Text</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">);<br></span></font></font></span><span style="font-family: 'Times New Roman', serif; font-size: 12px; ">}<br><span style="font-family: Simsun; font-size: medium; "><font face="Times New Roman, serif"><font color="#008000"><font size="2" style="font-size: 9pt"><span lang="zxx"><font color="#000000"><br></font>//
</span></font></font></font></span><span style="font-family: Simsun; font-size: medium; "><font color="#008000"><font size="2" style="font-size: 9pt">使用</font><font face="Times New Roman, serif"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null<br></span></font></font></font></span></span><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">delete_null</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">.</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">begin</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(),
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts1</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">.</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">end</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">());<br></span></font></font></span><font face="Times New Roman, serif"><font color="#008000"><font size="2" style="font-size: 9pt"><span lang="zxx">//
</span></font></font></font><font color="#008000"><font size="2" style="font-size: 9pt">使用</font><font face="Times New Roman, serif"><font size="2" style="font-size: 9pt"><span lang="zxx">deleter<br></span></font></font></font><span style="font-family: 'Times New Roman', serif; "><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">::</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">for_each</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts2</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">.</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">begin</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(),
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">uTexts2</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">.</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">end</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">(),
</span></font></font><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">deleter</span></font></font><font color="#000000"><font size="2" style="font-size: 9pt"><span lang="zxx">());</span></font></font></span></p>
<p style="margin-bottom: 0cm"><br>
</p>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">可以看到前者稍微简洁一些（包括最终的汇编代码），那么问题来了：为什么还需要代码量更大一些的</span><font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">deleter
</span></font></font></font><span lang="zh-CN">仿函数？</span></p>
<p style="margin-bottom: 0cm"><font face="Times New Roman, serif">	</font><span lang="zh-CN">理由是：</span><font color="#800000"><span lang="zh-CN">并不是所有存储堆对象的集合都是直接存储对象指针的</span></font><span lang="zh-CN">。比如可以将指针存储在</span><font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std::map</span></font></font></font><span lang="zh-CN">中&#8220;值&#8221;部分，甚至有些自定义集合只提供了遍历函数（类似于</span><font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">std::for_each</span></font></font></font><span lang="zh-CN">），但并不公开迭代器接口。在这些情况下，我们就可以使用</span><font face="Times New Roman, serif"><font color="#010001"><font size="2" style="font-size: 9pt"><span lang="zxx">deleter
</span></font></font></font><span lang="zh-CN">仿函数进行堆对象回收。</span></p>
<p style="margin-bottom: 0cm"><br>
</p>
<p style="margin-bottom: 0cm"><br></p><img src ="http://www.cppblog.com/xmli/aggbug/133653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-11-15 14:46 <a href="http://www.cppblog.com/xmli/archive/2010/11/15/133653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个Sqrt函数引发的血案（转）</title><link>http://www.cppblog.com/xmli/archive/2010/10/20/130576.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 20 Oct 2010 08:18:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/10/20/130576.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/130576.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/10/20/130576.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/130576.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/130576.html</trackback:ping><description><![CDATA[
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; "><br></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">注：我（转载本文的人）实测结果是sqrt（）函数要比<span  style="font-family: Georgia, 'Times New Roman', Times, san-serif; color: rgb(51, 51, 51); ">卡马克</span>的方法更快一些，测试环境xp sp2+ vc6 + stlport</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">-------------------------------------------------以下是转载原文</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">好吧，我承认我标题党了，不过既然你来了，就认真看下去吧，保证你有收获。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">我们平时经常会有一些数据运算的操作，需要调用sqrt，exp，abs等函数，那么时候你有没有想过：这个些函数系统是如何实现的？就拿最常用的sqrt函数来说吧，系统怎么来实现这个经常调用的函数呢？</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">虽然有可能你平时没有想过这个问题，不过正所谓是&#8220;临阵磨枪，不快也光&#8221;，你&#8220;眉头一皱，计上心来&#8221;，这个不是太简单了嘛，用二分的方法，在一个区间中，每次拿中间数的平方来试验，如果大了，就再试左区间的中间数；如果小了，就再拿右区间的中间数来试。比如求sqrt(16)的结果，你先试（0+16）/2=8，8*8=64，64比16大，然后就向左移，试（0+8）/2=4，4*4=16刚好，你得到了正确的结果sqrt(16)=4。然后你三下五除二就把程序写出来了：</p><span style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; line-height: 20px; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; ">float SqrtByBisection(float n) //用二分法 
{ 
	if(n&lt;0) //小于0的按照你需要的处理 
		return n; 
	float mid,last; 
	float low,up; 
	low=0,up=n; 
	mid=(low+up)/2; 
	do
	{
		if(mid*mid&gt;n)
			up=mid; 
		else 
			low=mid;
		last=mid;
		mid=(up+low)/2; 
	}while(abs(mid-last) &gt; eps);//精度控制
	return mid; 
} </pre></span><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">然后看看和系统函数性能和精度的差别（其中时间单位不是秒也不是毫秒，而是CPU Tick，不管单位是什么，统一了就有可比性）&nbsp;<br><img title="二分法性能对比" border="0" alt="二分法性能对比" src="http://blog.redfox66.com/image.axd?picture=image_thumb_5.png" width="373" height="170" complete="complete" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-color: initial; max-width: 610px; display: block; float: none; margin-left: auto; margin-right: auto; "></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">从图中可以看出，二分法和系统的方法结果上完全相同，但是性能上整整差了几百倍。为什么会有这么大的区别呢？难道系统有什么更好的办法？难道。。。。哦，对了，回忆下我们曾经的高数课，曾经老师教过我们&#8220;牛顿迭代法快速寻找平方根&#8221;，或者这种方法可以帮助我们，具体步骤如下：</p><p style="margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; word-break: break-all; font-size: 14px; line-height: 25px; border-bottom-color: rgb(225, 225, 225); border-bottom-width: 1px; border-bottom-style: solid; border-left-color: rgb(225, 225, 225); border-left-width: 1px; border-left-style: solid; background-color: rgb(241, 241, 241); border-top-color: rgb(225, 225, 225); border-top-width: 1px; border-top-style: solid; border-right-color: rgb(225, 225, 225); border-right-width: 1px; border-right-style: solid; font-family: Verdana, Arial, Helvetica, sans-serif; ">求出根号a的近似值：首先随便猜一个近似值x，然后不断令x等于x和a/x的平均数，迭代个六七次后x的值就已经相当精确了。&nbsp;<br>例如，我想求根号2等于多少。假如我猜测的结果为4，虽然错的离谱，但你可以看到使用牛顿迭代法后这个值很快就趋近于根号2了：&nbsp;<br>(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp; + 2/4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) / 2 = 2.25&nbsp;<br>(&nbsp;&nbsp;&nbsp;&nbsp; 2.25 + 2/2.25&nbsp;&nbsp;&nbsp;&nbsp; ) / 2 = 1.56944..&nbsp;<br>( 1.56944..+ 2/1.56944..) / 2 = 1.42189..&nbsp;<br>( 1.42189..+ 2/1.42189..) / 2 = 1.41423..&nbsp;<br>....<img title="sqrt" border="0" alt="sqrt" src="http://blog.redfox66.com/image.axd?picture=sqrt_thumb.gif" width="376" height="288" complete="complete" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-color: initial; max-width: 610px; display: block; float: none; margin-left: auto; margin-right: auto; "><br>这种算法的原理很简单，我们仅仅是不断用(x,f(x))的切线来逼近方程x^2-a=0的根。根号a实际上就是x^2-a=0的一个正实根，这个函数的导数是2x。也就是说，函数上任一点(x,f(x))处的切线斜率是2x。那么，x-f(x)/(2x)就是一个比x更接近的近似值。代入 f(x)=x^2-a得到x-(x^2-a)/(2x)，也就是(x+a/x)/2。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">相关的代码如下：</p><span style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; line-height: 20px; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; ">float SqrtByNewton(float x)
{
	float val = x;//最终
	float last;//保存上一个计算的值
	do
	{
		last = val;
		val =(val + x/val) / 2;
	}while(abs(val-last) &gt; eps);
	return val;
}</pre></pre></span><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">然后我们再来看下性能测试：</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; "><img title="image" border="0" alt="image" src="http://blog.redfox66.com/image.axd?picture=image_thumb_6.png" width="375" height="180" complete="complete" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-color: initial; max-width: 610px; display: block; float: none; margin-left: auto; margin-right: auto; "></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">哇塞，性能提高了很多，可是和系统函数相比，还是有这么大差距，这是为什么呀？想啊想啊，想了很久仍然百思不得其解。突然有一天，我在网上看到一个神奇的方法，于是就有了今天的这篇文章，废话不多说，看代码先：</p><span style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; line-height: 20px; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; ">float InvSqrt(float x)
{
	float xhalf = 0.5f*x;
	int i = *(int*)&amp;x; // get bits for floating VALUE 
	i = 0x5f375a86- (i&gt;&gt;1); // gives initial guess y0
	x = *(float*)&amp;i; // convert bits BACK to float
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy

	return 1/x;
}
</pre></pre></span><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">然后我们最后一次来看下性能测试：</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; "><img title="image" border="0" alt="image" src="http://blog.redfox66.com/image.axd?picture=image_thumb_7.png" width="393" height="210" complete="complete" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-color: initial; max-width: 610px; display: block; float: none; margin-left: auto; margin-right: auto; ">这次真的是质变了，结果竟然比系统的还要好。。。哥真的是震惊了！！！哥吐血了！！！一个函数引发了血案！！！血案，血案。。。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">到现在你是不是还不明白那个&#8220;鬼函数&#8221;，到底为什么速度那么快吗？不急，先看看下面的故事吧：</p><div style="border-left-color: rgb(195, 227, 238); padding-bottom: 5px; background-color: rgb(236, 244, 252); margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px; padding-left: 5px; padding-right: 5px; padding-top: 5px; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; line-height: 20px; "><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">Quake-III Arena (雷神之锤3)是90年代的经典游戏之一。该系列的游戏不但画面和内容不错，而且即使计算机配置低，也能极其流畅地运行。这要归功于它3D引擎的开发者约翰-卡马克（John Carmack）。事实上早在90年代初DOS时代，只要能在PC上搞个小动画都能让人惊叹一番的时候，John Carmack就推出了石破天惊的Castle Wolfstein, 然后再接再励，doom, doomII, Quake...每次都把3-D技术推到极致。他的3D引擎代码资极度高效，几乎是在压榨PC机的每条运算指令。当初MS的Direct3D也得听取他的意见，修改了不少API。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">最近，QUAKE的开发商ID SOFTWARE 遵守GPL协议，公开了QUAKE-III的原代码，让世人有幸目睹Carmack传奇的3D引擎的原码。这是QUAKE-III原代码的下载地址：&nbsp;<br><a href="http://www.fileshack.com/file.x?fid=7547" style="text-decoration: underline; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; color: rgb(10, 93, 10); ">http://www.fileshack.com/file.x?fid=7547</a></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">(下面是官方的下载网址，搜索 &#8220;quake3-1.32b-source.zip&#8221; 可以找到一大堆中文网页的。<a href="ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip)" style="text-decoration: underline; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; color: rgb(10, 93, 10); ">ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip)</a></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">我们知道，越底层的函数，调用越频繁。3D引擎归根到底还是数学运算。那么找到最底层的数学运算函数（在game/code/q_math.c）， 必然是精心编写的。里面有很多有趣的函数，很多都令人惊奇，估计我们几年时间都学不完。在game/code/q_math.c里发现了这样一段代码。它的作用是将一个数开平方并取倒，经测试这段代码比(float)(1.0/sqrt(x))快4倍：</p><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; ">float Q_rsqrt( float number )
{
	long i;
	float x2, y;
	const float threehalfs = 1.5F;

	x2 = number * 0.5F;
	y   = number;
	i   = * ( long * ) &amp;y;   // evil floating point bit level hacking
	i   = 0x5f3759df - ( i &gt;&gt; 1 ); // what the fuck?
	y   = * ( float * ) &amp;i;
	y   = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
	// y   = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed

	#ifndef Q3_VM
	#ifdef __linux__
		 assert( !isnan(y) ); // bk010122 - FPE?
	#endif
	#endif
	return y;
}  </pre></pre><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">函数返回1/sqrt(x)，这个函数在图像处理中比sqrt(x)更有用。&nbsp;<br>注意到这个函数只用了一次叠代！（其实就是根本没用叠代，直接运算）。编译，实验，这个函数不仅工作的很好，而且比标准的sqrt()函数快4倍！要知道，编译器自带的函数，可是经过严格仔细的汇编优化的啊！&nbsp;<br>这个简洁的函数，最核心，也是最让人费解的，就是标注了&#8220;what the fuck?&#8221;的一句&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i = 0x5f3759df - ( i &gt;&gt; 1 );</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">再加上y&nbsp; = y * ( threehalfs - ( x2 * y * y ) );&nbsp;<br>两句话就完成了开方运算！而且注意到，核心那句是定点移位运算，速度极快！特别在很多没有乘法指令的RISC结构CPU上，这样做是极其高效的。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">算法的原理其实不复杂,就是牛顿迭代法,用x-f(x)/f'(x)来不断的逼近f(x)=a的根。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">没错，一般的求平方根都是这么循环迭代算的但是卡马克(quake3作者)真正牛B的地方是他选择了一个神秘的常数0x5f3759df 来计算那个猜测值，就是我们加注释的那一行，那一行算出的值非常接近1/sqrt(n)，这样我们只需要2次牛顿迭代就可以达到我们所需要的精度。好吧如果这个还不算NB,接着看:</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">普渡大学的数学家Chris Lomont看了以后觉得有趣，决定要研究一下卡马克弄出来的这个猜测值有什么奥秘。Lomont也是个牛人，在精心研究之后从理论上也推导出一个最佳猜测值，和卡马克的数字非常接近, 0x5f37642f。卡马克真牛，他是外星人吗？</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">传奇并没有在这里结束。Lomont计算出结果以后非常满意，于是拿自己计算出的起始值和卡马克的神秘数字做比赛，看看谁的数字能够更快更精确的求得平方根。结果是卡马克赢了... 谁也不知道卡马克是怎么找到这个数字的。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">最后Lomont怒了，采用暴力方法一个数字一个数字试过来，终于找到一个比卡马克数字要好上那么一丁点的数字，虽然实际上这两个数字所产生的结果非常近似，这个暴力得出的数字是0x5f375a86。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">Lomont为此写下一篇论文，"Fast Inverse Square Root"。 论文下载地址：&nbsp;<br><a href="http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf" style="text-decoration: underline; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; color: rgb(10, 93, 10); ">http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf</a>&nbsp;<br><a href="http://www.matrix67.com/data/InvSqrt.pdf" style="text-decoration: underline; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; color: rgb(10, 93, 10); ">http://www.matrix67.com/data/InvSqrt.pdf</a></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">参考：&lt;IEEE Standard 754 for Binary Floating-Point Arithmetic&gt;&lt;FAST INVERSE SQUARE ROOT&gt;</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">最后，给出最精简的1/sqrt()函数：</p><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; ">float InvSqrt(float x)
{
	float xhalf = 0.5f*x;
	int i = *(int*)&amp;x; // get bits for floating VALUE 
	i = 0x5f375a86- (i&gt;&gt;1); // gives initial guess y0
	x = *(float*)&amp;i; // convert bits BACK to float
	x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
	return x;
}  </pre></pre><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">大家可以尝试在PC机、51、AVR、430、ARM、上面编译并实验，惊讶一下它的工作效率。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; ">前两天有一则新闻，大意是说 Ryszard Sommefeldt 很久以前看到这么样的一段 code (可能出自 Quake III 的 source code)：</p><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; "><pre style="font-family: Verdana, Arial, Helvetica, sans-serif; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(204, 204, 204); border-right-color: rgb(204, 204, 204); border-bottom-color: rgb(204, 204, 204); border-left-color: rgb(204, 204, 204); font-size: 13px; margin-top: 3px; margin-right: 3px; margin-bottom: 3px; margin-left: 3px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: none; background-attachment: scroll; background-origin: initial; background-clip: initial; background-color: rgb(248, 248, 248); line-height: 1.5; background-position: 0% 50%; background-repeat: repeat repeat; ">float InvSqrt (float x) 
{
	float xhalf = 0.5f*x;
	int i = *(int*)&amp;x;
	i = 0x5f3759df - (i&gt;&gt;1);
	x = *(float*)&amp;i;
	x = x*(1.5f - xhalf*x*x);
	return x;
}</pre></pre>他一看之下惊为天人，想要拜见这位前辈高人，但是一路追寻下去却一直找不到人；同时间也有其他人在找，虽然也没找到出处，但是 Chris Lomont 写了一篇论文 (in PDF) 解析这段 code 的算法 (用的是 Newton&#8217;s Method，牛顿法；比较重要的是后半段讲到怎么找出神奇的 0x5f3759df 的)。&nbsp;<br>PS. 这个 function 之所以重要，是因为求 开根号倒数 这个动作在 3D 运算 (向量运算的部份) 里面常常会用到，如果你用最原始的 sqrt() 然后再倒数的话，速度比上面的这个版本大概慢了四倍吧&#8230; XD&nbsp;<br>PS2. 在他们追寻的过程中，有人提到一份叫做 MIT HACKMEM 的文件，这是 1970 年代的 MIT 强者们做的一些笔记 (hack memo)，大部份是 algorithm，有些 code 是 PDP-10 asm 写的，另外有少数是 C code (有人整理了一份列表)</div><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">好了，故事就到这里结束了，希望大家能有有收获:)</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; ">下载代码</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; word-break: break-all; font-size: 14px; line-height: 25px; font-family: Verdana, Arial, Helvetica, sans-serif; "><a href="http://blog.redfox66.com/file.axd?file=2010%2f10%2fTestSqrt.cpp" style="text-decoration: underline; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; color: rgb(10, 93, 10); ">TestSqrt.cpp (2.58 kb)</a></p><img src ="http://www.cppblog.com/xmli/aggbug/130576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-10-20 16:18 <a href="http://www.cppblog.com/xmli/archive/2010/10/20/130576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>奔腾指令速查手册(转)</title><link>http://www.cppblog.com/xmli/archive/2010/09/26/127781.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Sun, 26 Sep 2010 09:51:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/09/26/127781.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/127781.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/09/26/127781.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/127781.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/127781.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 指令名称指令形式机器码标志位(设置/测试)说&nbsp;&nbsp;&nbsp;&nbsp;明应用举例ES:ES:26　ES段跨越前缀　CS:CS:2E　CS段跨越前缀　SS:SS:36　SS段跨越前缀　DS:DS:3E　DS段跨越前缀　FS:FS:64　FS段跨越前缀　GS:GS:65　GS段跨越前缀　Opsize:Opsize:66　操作数类型跨越前缀　Address:Address:67　地...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2010/09/26/127781.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/127781.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-09-26 17:51 <a href="http://www.cppblog.com/xmli/archive/2010/09/26/127781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>语言的歧义（转）</title><link>http://www.cppblog.com/xmli/archive/2010/03/12/109485.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 12 Mar 2010 02:19:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/03/12/109485.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/109485.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/03/12/109485.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/109485.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/109485.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 我们可以看到很多C语言相关的一些东西。比如《语言的歧义》主要告诉了大家C语言中你意想不到的错误以及一些歧义上的东西。而《谁说C语言很简单》则通过一些看似你从来不可能写出的代码来告诉大家C语言并不是一件容易事情。《6个变态的hello world》和《如何弄乱C的源代码》则以一种极端的方式告诉大家，不要以为咱们自己写不出混乱的代码，每个程序员其实都有把代码搞得一团乱的潜质。通过这些文章，相信你对编程...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2010/03/12/109485.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/109485.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-03-12 10:19 <a href="http://www.cppblog.com/xmli/archive/2010/03/12/109485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何通过程序屏蔽windows粘滞键之热键</title><link>http://www.cppblog.com/xmli/archive/2010/03/04/108880.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 04 Mar 2010 05:36:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/03/04/108880.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/108880.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/03/04/108880.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/108880.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/108880.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 最近在游戏开发中遇到这样的需求：玩家需要不停的连击shift键。<br>&nbsp;&nbsp;&nbsp; 大家都知道，在windows下默认连击shift键到5次会弹出粘滞键设置的对话框，如下图所示。<br><br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/xmli/stickykeys.JPG" width=410 height=160><br><br>&nbsp;&nbsp;&nbsp; 在游戏中必须屏蔽windows对粘滞键热键的反应，否则游戏体验将会非常差。在网上查找一段时间后发现，关于如何通过程序屏蔽粘滞键热键的中文资料非常少，大多数的答案都是打开&#8220;控制面板&#8221;，然后找到&#8220;辅助功能选项&#8221;条目云云，这显示不是程序员所需要的解决手法。<br>&nbsp;&nbsp;&nbsp; 经过多方查找，问题最终得以解决。下面列表代码，以飨未来者，程序VC6.0+XP系统下运行通过。<br><br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;activate&nbsp;or&nbsp;inactivate&nbsp;sticky&nbsp;keys&nbsp;hot&nbsp;key&nbsp;(pop&nbsp;up&nbsp;&nbsp;a&nbsp;set&nbsp;up&nbsp;dialog&nbsp;after&nbsp;continually&nbsp;pressed&nbsp;shift&nbsp;5&nbsp;times)</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;ActivateStickyHotkey(</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;isActivate,&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pIsPreviouslyActivate</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;fetch&nbsp;current&nbsp;sticky&nbsp;keys&nbsp;state</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STICKYKEYS&nbsp;skf;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;DWORD&nbsp;datasize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(STICKYKEYS);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skf.cbSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;datasize;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">SystemParametersInfo(SPI_GETSTICKYKEYS,&nbsp;datasize,&nbsp;(LPVOID)</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">skf,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;isPreviouslyActivate&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(skf.dwFlags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;SKF_HOTKEYACTIVE)&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;store&nbsp;old&nbsp;sticky&nbsp;keys&nbsp;state</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(NULL</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;pIsPreviouslyActivate)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pIsPreviouslyActivate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;isPreviouslyActivate;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;return&nbsp;true&nbsp;if&nbsp;no&nbsp;need&nbsp;to&nbsp;change&nbsp;state</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(isActivate</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">isPreviouslyActivate)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;change&nbsp;sticky&nbsp;keys&nbsp;state</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skf.dwFlags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;isActivate</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">&nbsp;(skf.dwFlags&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;SKF_HOTKEYACTIVE)&nbsp;:&nbsp;(skf.dwFlags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">~</span><span style="COLOR: #000000">SKF_HOTKEYACTIVE));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;isSuccess&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;TRUE</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">SystemParametersInfo(SPI_SETSTICKYKEYS,&nbsp;datasize,&nbsp;(LPVOID)</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">skf,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;isSuccess;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span></div>
<br>参考资料：<br>1, <a href="http://msdn.microsoft.com/en-us/library/aa925903.aspx">http://msdn.microsoft.com/en-us/library/aa925903.aspx</a>&nbsp;<br>2, <a href="http://stackoverflow.com/questions/734618/disabling-accessibility-shortcuts-in-net-application">http://stackoverflow.com/questions/734618/disabling-accessibility-shortcuts-in-net-application</a>
<img src ="http://www.cppblog.com/xmli/aggbug/108880.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-03-04 13:36 <a href="http://www.cppblog.com/xmli/archive/2010/03/04/108880.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2009年终奖个税计算器 python</title><link>http://www.cppblog.com/xmli/archive/2010/02/11/107713.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 11 Feb 2010 07:32:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/02/11/107713.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/107713.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/02/11/107713.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/107713.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/107713.html</trackback:ping><description><![CDATA[&nbsp;看看你的年终奖发的是否合理：<br><br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">def</span><span style="COLOR: #000000">&nbsp;getTaxRatio(taxSalary):<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;ratiolist&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">(<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">500</span><span style="COLOR: #000000">,&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">0.05</span><span style="COLOR: #000000">,&nbsp;0),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">2000</span><span style="COLOR: #000000">,&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">0.10</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">25</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">5000</span><span style="COLOR: #000000">,&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">0.15</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">125</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">20000</span><span style="COLOR: #000000">,&nbsp;&nbsp;</span><span style="COLOR: #000000">0.20</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">375</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">40000</span><span style="COLOR: #000000">,&nbsp;&nbsp;</span><span style="COLOR: #000000">0.25</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">1375</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">60000</span><span style="COLOR: #000000">,&nbsp;&nbsp;</span><span style="COLOR: #000000">0.30</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">3375</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">80000</span><span style="COLOR: #000000">,&nbsp;&nbsp;</span><span style="COLOR: #000000">0.35</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">6375</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">100000</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0.40</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">10375</span><span style="COLOR: #000000">),<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #000000">9999999</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0.45</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">15375</span><span style="COLOR: #000000">)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;taxSalary</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">&nbsp;0:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;ratio&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;ratiolist:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;taxSalary</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">&nbsp;ratio[0]:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(ratio[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">],&nbsp;ratio[</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">])<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;(0,&nbsp;0)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">def</span><span style="COLOR: #000000">&nbsp;calcYearAwardTax(yearAward):<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;monthAward&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;yearAward&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">12</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;taxRatio,&nbsp;taxAdjust&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;getTaxRatio(monthAward)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;tax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;yearAward&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;taxRatio&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;taxAdjust<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;resultAward&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;yearAward&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;tax<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;retcode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(yearAward,&nbsp;resultAward,&nbsp;tax,&nbsp;taxRatio,&nbsp;taxAdjust)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">#</span><span style="COLOR: #008000">print('yearAward=&nbsp;%d,&nbsp;resultAward=&nbsp;%f,&nbsp;tax=&nbsp;%f,&nbsp;taxRatio=&nbsp;%f,&nbsp;taxAdjust=&nbsp;%d'%retcode)</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;retcode<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">#</span><span style="COLOR: #008000">###############################################################################</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000">&nbsp;sys<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000">&nbsp;os<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">argNum&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;len(sys.argv)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;argNum&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;scriptName&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;os.path.basename(</span><span style="COLOR: #800080">__file__</span><span style="COLOR: #000000">)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">print</span><span style="COLOR: #000000">(</span><span style="COLOR: #800000">'</span><span style="COLOR: #800000">usage1:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;awardUpperBound</span><span style="COLOR: #800000">'</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">scriptName)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">print</span><span style="COLOR: #000000">(</span><span style="COLOR: #800000">'</span><span style="COLOR: #800000">usage2:&nbsp;&nbsp;&nbsp;&nbsp;%s&nbsp;awardLowerBound&nbsp;awardUpperBound</span><span style="COLOR: #800000">'</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">scriptName)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;sys.exit()<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">print</span><span style="COLOR: #000000">(</span><span style="COLOR: #800000">'</span><span style="COLOR: #800000">-----------------------------------------------------------------------</span><span style="COLOR: #800000">'</span><span style="COLOR: #000000">)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">awardLowerBound,&nbsp;awardUpperBound&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;0,&nbsp;0<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;argNum&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;awardUpperBound&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;int(sys.argv[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">])<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">elif</span><span style="COLOR: #000000">&nbsp;argNum&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;awardLowerBound,&nbsp;awardUpperBound&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;int(sys.argv[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]),&nbsp;int(sys.argv[</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">])<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;awardLowerBound</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;awardUpperBound:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;swap(awardLowerBound,&nbsp;awardUpperBound)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">perfectAward,&nbsp;perfectGain&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;0,&nbsp;0<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;award&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;range(awardLowerBound,&nbsp;awardUpperBound&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">):<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;retcode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;calcYearAwardTax(award)&nbsp;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;len(retcode)&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;retcode[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;perfectGain:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perfectAward&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;retcode[0]<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perfectGain&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;retcode[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;award</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;awardUpperBound:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">print</span><span style="COLOR: #000000">(</span><span style="COLOR: #800000">'</span><span style="COLOR: #800000">upperBoundAward=&nbsp;%d,\tgain=&nbsp;%f,\ttax=&nbsp;%f</span><span style="COLOR: #800000">'</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">(retcode[0],&nbsp;retcode[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">],&nbsp;retcode[0]&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;retcode[</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]))&nbsp;&nbsp;&nbsp;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">print</span><span style="COLOR: #000000">(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">perfectAward=&nbsp;%d,\tgain=&nbsp;%f,\ttax=&nbsp;%f</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">(perfectAward,&nbsp;perfectGain,&nbsp;perfectAward&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;perfectGain))<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<br>保存成文件 awardTax.py 然后在命令行调用即可。<br>比如你年终奖发了25000，则：<br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/xmli/yearAwardTax.JPG" width=525 height=77><br>ok, 那么你亏了，你比拿24000的同志多交税3625-2375= 1250元，最终收益还比他少21625-21375= 250元。<br>这个数值段最佳的年终奖数额为24000，因此还是向老板申请少给你发点年终奖吧。
<img src ="http://www.cppblog.com/xmli/aggbug/107713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-02-11 15:32 <a href="http://www.cppblog.com/xmli/archive/2010/02/11/107713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何保持软件开发团队的稳定性  （转）</title><link>http://www.cppblog.com/xmli/archive/2010/02/11/107699.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 11 Feb 2010 03:32:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2010/02/11/107699.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/107699.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2010/02/11/107699.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/107699.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/107699.html</trackback:ping><description><![CDATA[<span  style="border-collapse: collapse; color: rgb(68, 68, 68); font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 12px; "><table cellspacing="0" cellpadding="0" style="word-wrap: break-word; empty-cells: show; border-collapse: collapse; line-height: normal; table-layout: fixed; margin-left: 1px; width: 600px; "><tbody style="word-wrap: break-word; line-height: normal; "><tr style="word-wrap: break-word; line-height: normal; "><td class="t_msgfont" id="postmessage_2132150" style="word-wrap: break-word; color: rgb(68, 68, 68); font: normal normal normal 12px/1.6em Verdana, Helvetica, Arial, sans-serif; line-height: 1.6em; font-size: 14px; ">　　一个公司想要获得成功，有两个基本点：一个是好的人才;一个是好的业务。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　好的业务能够吸引到好的人才;而好的人才也能创造出好的业务。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　成功的公司在这方面形成了良性循环，相反糟糕的公司形成了恶性循环。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　保持团队的稳定性说来容易，其实对于每一个优秀的研发经理和公司<span href="tag.php?name=CEO" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">CEO</span>都非常具有挑战性，尤其是<span href="tag.php?name=%D4%B1%B9%A4" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">员工</span>很多时候并不能意识到这一点和理解领导层的压力。就好比单身汉不能理解父亲的心情一样。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　然而在家庭的未来面前，父亲责无旁贷。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　团队稳定性涉及到很多方面的内容，比如公司文化、员工激励、招聘流程、团队建设、职业规划、技能培训和备份等。下面谈谈个人的感想。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　招聘<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　招聘是团队建设的源头，一个公司从无到有，从小到大，每一步成长都离不开好的招聘。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　您应该尽可能避免在源头上就引入了不稳定的因素，这就需要制定完善的招聘流程和人事制度并不断根据市场情况作出调整。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　比如您带领的是500强公司中的研发团队，那么您需要的人才最好不要太有个人想法和<span href="tag.php?name=%B4%B4%D2%B5" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">创业</span>的精神，在大部分情况下，您要招聘的是偏好工作稳定性、好的薪酬福利以及开放的<span href="tag.php?name=%C6%F3%D2%B5" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">企业</span>文化的员工。而为一个创业公司招募员工则不尽相同。下面是一个简单的checklist，来设定一些问答测验。不要认为测验很准，因为应聘者可能会撒谎或者迎合<span href="tag.php?name=%C3%E6%CA%D4" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">面试</span>官。但没有测验，您完全主观的判断会给公司带来更大的紊乱和随机性：<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)上班地点和居住地距离(异地上班几乎都是权宜之计;而超过1个半小时路程且家庭地址固定的也需要慎重考虑)<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)朋友在哪里工作?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)对<span href="tag.php?name=%C9%CF%BA%A3" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">上海</span><span href="tag.php?name=%C9%FA%BB%EE" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">生活</span>成本如何看待?是否曾经考虑过回到家乡二线城市发展?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)是否有考研、移民或出国深造的打算?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)是否有更好的Offer?(如果有的话，根据公司实际情况衡量一下是否可以提供有竞争力的薪酬和福利)<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)如果是创业公司，可以询问对方是否有承担压力和风险的心理素质和客观能力;如果是大公司则相反需要探询对方是否正在规划创业、是否有更大的个人理想?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)对公司薪水、福利、期权和<span href="tag.php?name=%B9%C9%C6%B1" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">股票</span>如何看待?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)偏好什么样的企业文化?能否接受项目原因的加班?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)个人规划和公司职位之间是否比较契合，是否愿意根据公司实际情况调整自己的技术方向和学习新的技术?<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　总之，把握好招聘，您就成功了一半。这一点很难。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　企业文化<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　好的<span href="tag.php?name=%B9%DC%C0%ED" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">管理</span>者除了熟知管理科学(比如软件开发领域的开发模式、项目管理理论)和拥有丰富的实践外，<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　您必须做到洞悉并承认人性的弱点，而不是逆天行道。管理者要有比大多数技术人员更高的情商。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　要构建好的企业文化，您可能需要考虑如下几点：<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)大部分人都不喜欢加班，因为工作只是生活的一部分，每个人都有亲人、朋友和个人爱好，在工作上花费<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　更多的时间就意味着牺牲一部分生活。倡导生活和工作之间的平衡毫无疑问是一个吸引人的公司文化。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)大部分人都希望有一个轻松开放的工作氛围，不喜欢被kick ass着工作。那么除非很有必要，不要轻易去踢员工的&#8220;屁股&#8221;。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　您可以通过喝下午茶、聊一些家常、组织集体活动、体育比赛、旅游、生日party等方式来活跃团队氛围。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)大部分人都喜欢被表扬而不是训斥。那么切忌不要当着很多员工的面训斥一名员工。而不要吝啬对员工做得好的地方给予鼓励，效果超出您的想象。对于错误的地方，从帮助员工提高和改进的地方针对事情本身详细列出错误和建议，切忌不要下一个员工无能这样空洞而简单的结论。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　*)优秀的人喜欢从事有挑战性的工作，不愿意重复单调机械的工作。给优秀员工设定一个120%的目标，鼓励员工完成并给以适当的激励。让优秀员工主导新项目的开发或新技术的调研，充分调动员工的积极性和工作热情。给有领导力的员工带领team和project的机会，并着力培养他们，使他们成为您的得力助手，当然您要注意员工的能力是一方面，品德更关键，过河拆桥、不诚信的人，您永远不要给他机会。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　激励<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　激励的方式有很多种。对于创业公司而言，最富激励性和想象空间的无非是期权。可能薪水很低，但是承诺较多的期权。当员工想象着通过自己以及大家的艰苦奋斗，公司2、3年内能够登录Nasdaq时，动力是无穷的。如果能够登录<span href="tag.php?name=%D6%D0%B9%FA" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">中国</span>的创业板，动力会更高，因为我们的创业板市盈率高，有大批无私奉献且极具投机精神的<span href="tag.php?name=%B9%C9%C3%F1" onclick="tagshow(event)" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 0, 0); white-space: nowrap; ">股民</span>撑腰。这些期权通常是分成若干年兑现的，因此可以凝聚员工在一家公司长期工作。对于已上市公司而言，比较好的方式是给绩效优秀的员工股票奖励。除了期权/股票之外，您还可以设定合理的项目奖、季度奖、年终奖、优秀部门活动经费、加班补贴等措施。最后但也许是最重要的一点，领导者要在精神层面上不断鼓舞团队，要有远大的理想和描绘诱人的蓝图，要有坚定的成功信念，要相信您的团队正在从事着一项伟大的事业，每位员工都将成为公司元勋。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　职业规划和培训<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　要关心每一位员工的成长。给他们成长的空间。这包括技术方面的培训，也包括职位的上升。大的公司可以提供英语/日语培训、付费技术培训。小的创业公司可以鼓励大家互相学习，知识共享，营造学习型的团队。职业规划要按照每个人的技术特点、性格和能力来制定。通常有技术型方向(se-sse-&gt;designer-&gt;architecture-&gt;master-&gt;scientist)、管理型方向(以开发为例：se-&gt;sse-&gt;team leader-&gt;project manager-&gt;department manager-&gt;section manager-&gt;director-&gt;CEO)、技术管理方向(se-&gt;sse-&gt;technical leader or development leader-&gt;R&amp;D manager-&gt;technical director-&gt;CTO)。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　合理的流动性和技术备份<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　人往高处走，水往低处流。社会规律决定了公司员工不可能一成不变。在强调稳定的同时也鼓励合理的流动(包括公司内部岗位的重新选择)。这出于两个方面的原因。一个是员工追求更好的个人发展而选择另外的产品线或者跳槽。一个是公司认为员工不称职而进行合理的职位调整或淘汰。作为团队管理者而言，最幸运的事情莫过于有一群有责任心、有能力而且能够朝夕和谐相处的员工。但优秀员工的成长速度，常常会超出公司的成长速度，现有的环境无法提供更好的机遇和空间给优秀的员工，这样的员工早晚会有自己的选择。公司管理者能够做的是一方面尽可能给出更好的薪酬以延长这个时间，使员工能够最大程度为公司创造价值;另一方面要做好关键技术的备份工作，骨干员工需要承担起更多的技术培训和知识共享的任务，把自己的知识技能更好的传达给下面的工程师，避免离开后给公司或团队带来过大的冲击。能力差、工作态度消极的员工则面临着被公司辞退的风险，对于影响了整个团队氛围和工作效率的员工，要紧定的予以减薪、降职和劝退，但一定要客观，有事实依据，而不能按照个人喜好来做决定。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　在如今猎头、招聘网站给了我们每个人更多选择机会去改变自己命运的同时，也让部分人变得心浮气躁，朝秦暮楚而最终迷失方向。<br style="word-wrap: break-word; line-height: normal; "><br style="word-wrap: break-word; line-height: normal; ">　　我们无法去改变这个现状，但是我们可以通过一点一滴的努力来尽量降低人员变动方面的风险，来让员工尽可能有家的归属感，能感受到那份激情和温暖、能够风雨同舟一路相伴。虽然我们都清楚，企业不是你我永远的家。&nbsp;</td></tr></tbody></table></span>
<img src ="http://www.cppblog.com/xmli/aggbug/107699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2010-02-11 11:32 <a href="http://www.cppblog.com/xmli/archive/2010/02/11/107699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载—网络游戏程序中解决加载卡顿的有效方法</title><link>http://www.cppblog.com/xmli/archive/2009/12/30/104448.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 30 Dec 2009 06:00:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/12/30/104448.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/104448.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/12/30/104448.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/104448.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/104448.html</trackback:ping><description><![CDATA[<span  style="font-family: Arial; font-size: 14px; line-height: 21px; ">对于3d视频游戏来说，游戏引擎的性能是至关重要的。玩家在体验一款游戏时，游戏的流畅度是最基本的要求。与单机游戏不同，网络游戏更需要考虑性能问题，因为无法像单机游戏那样，控制游戏元素的复杂度来达到效率的要求。大量玩家涌入同一片区域，同屏出现大量的游戏角色是无法避免的，因此游戏帧率的大幅下降，系统资源的大量消耗也很难避免，这是网络游戏引擎最难处理的问题之一。<br>&nbsp;&nbsp;&nbsp;&nbsp;这里要讲一下游戏帧率的控制，通常玩家在玩游戏抱怨游戏客户端卡有两个意思，一是游戏平均帧率很低，二是游戏的帧率非常不稳，导致了卡顿。实际上游戏平均帧率低，对玩家心情的影响远不及卡顿造成的影响。平均10帧的游戏，虽然已经很糟糕了，但是依然能玩，但是频繁的卡顿给人的感觉就糟糕透了，平时40帧左右的游戏忽然因为加载个什么东西卡了一下，帧率掉到了零点几，然后又恢复到40帧，这种卡顿给人的感觉就是烦透了。<br>&nbsp;&nbsp;&nbsp;&nbsp;游戏引擎首要解决的性能问题就是卡顿的问题。要解决卡顿的话需要做到以下两点：<br>&nbsp;&nbsp;&nbsp;&nbsp;第一，不要在主线程去加载资源，最忌讳的操作就是打开文件，这个操作会挂起当前线程，也就是说会让渲染线程停顿。把所有的资源加载操作全放在加载线程去做，毕竟加载线程随便停顿也没什么关系，对主线程的渲染没影响，主线程只需要每帧判断资源是否已经加载上来就可以了。一但发现已经加载上来了，就可以用这个数据去渲染了。<br>&nbsp;&nbsp;&nbsp;&nbsp;第二，也是最重要的一点，把加载的操作摊到多帧去做。通常角色走进人堆里以后，或者在战场上魔法漫天飞的时候，服务器会传来大量消息需要处理，最典型的就是创建消息，无论是创建角色还创建特效，就算是采用多线程加载的方式，在一帧内创建对象，通知线程加载底层资源，那么多消息的处理依然会不可避免地造成卡顿。这里有一个非常好的解决办法，就是这些处理消息的操作不要一帧内做完，而是分摊到多帧完成。<br>&nbsp;&nbsp;&nbsp;&nbsp;一般说来，处理网络消息的过程是这样一个循环：<br>while(&nbsp;消息队列中还有消息&nbsp;)<br>{<br>&nbsp;&nbsp;&nbsp;从队列中取出第一条消息;<br>&nbsp;&nbsp;&nbsp;处理这条消息;<br>&nbsp;&nbsp;&nbsp;将这个消息从队列中删除;<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp;在一帧当中，循环遍历整个消息队列，将这一帧收到的消息一个一个处理一遍。<br>&nbsp;&nbsp;&nbsp;&nbsp;这样做忽略了最重要的效率问题，当你因为游戏卡顿在焦头烂额地优化资源加载时，不放考虑修改一下消息队列的处理。<br>&nbsp;&nbsp;&nbsp;&nbsp;在这里，我可以加入计时：GetTickCount()<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 14px; margin-left: 0px; ">&nbsp;</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 14px; margin-left: 0px; ">初始时间&nbsp;=&nbsp;GetTickCount();<br>while(&nbsp;消息队列中还有消息&nbsp;)<br>{<br>&nbsp;&nbsp;&nbsp;从队列中取出第一条消息；<br>&nbsp;&nbsp;&nbsp;处理这条消息；<br>&nbsp;&nbsp;&nbsp;当前时间&nbsp;=&nbsp;GetTickCount();<br>&nbsp;&nbsp;&nbsp;经过时间&nbsp;=&nbsp;当前时间&nbsp;-&nbsp;初始时间;<br>&nbsp;&nbsp;&nbsp;if(&nbsp;经过时间&nbsp;&gt;&nbsp;20&nbsp;)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;}<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp;如果这一帧的处理时间超过了20ms，则把剩下的消息放到下一帧处理。通过这种计时的方式，你会发现游戏的流畅度简直有了天翻地覆的变化！在优化之前，有个几个人在用群攻魔法攻击大量的怪物，这些家伙忽然涌入到视野中，帧率便一下掉到了零点几，游戏出现了非常严重的卡顿，这种状态持续了很短一段时间，帧率又迅速回升上去。而现在，经过修改以后，你会发现那些家伙很平滑地出现在视野中，没有一丝的卡顿。如此效果简直是奇迹一般，而这一切仅仅是修改了几行代码而已。<br>&nbsp;&nbsp;&nbsp;&nbsp;现在考虑这么做所带来的问题。如果消息量非常大，而机器又慢，平均帧率又很低的话，那麻烦可就大了：每帧处理的消息量还没有收到的消息量大。这可是个很严重的问题，这会让客户端的表现与实际情况严重脱节。在这里，就需要有一个机制，保证消息在积攒超过一定数量时，能得到及时的处理：</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 14px; margin-left: 0px; ">初始时间&nbsp;=&nbsp;GetTickCount();<br>while(&nbsp;消息队列中还有消息&nbsp;)<br>{<br>&nbsp;&nbsp;&nbsp;if(&nbsp;消息队列中的消息数量&nbsp;&gt;&nbsp;300&nbsp;)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一次性处理所有的消息;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从队列中取出第一条消息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;处理这条消息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当前时间&nbsp;=&nbsp;GetTickCount();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;经过时间&nbsp;=&nbsp;当前时间&nbsp;-&nbsp;初始时间;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;经过时间&nbsp;&gt;&nbsp;20&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp;这样就解决了消息越积攒越多的问题，当消息越攒越多时，会一次性处理所有的消息。但这样也会带来一个问题，那就是在帧率比较低的机器上，当需要处理的消息特别多时会出现周期性的卡顿。卡顿的原因就是那步一次性处理所有消息的操作。优化的目的就是要避免这样的卡顿，而对于低端机器来说，这样的优化不但没有起到效果，反而加重了卡顿现象。为了弥补这个方法带来的弊端，就要对那个经过时间20ms做点手脚：</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 14px; margin-left: 0px; ">static&nbsp;时间阈值&nbsp;=&nbsp;20;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//注意时间阈值是static的<br>if(&nbsp;消息队列中的消息数量&nbsp;&gt;&nbsp;100&nbsp;)<br>{<br>&nbsp;&nbsp;&nbsp;++时间阈值;<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp;--时间阈值;<br>}<br>if(&nbsp;时间阈值&nbsp;&lt;&nbsp;20&nbsp;)<br>&nbsp;&nbsp;&nbsp;时间阈值&nbsp;=&nbsp;20;<br>if(&nbsp;时间阈值&nbsp;&gt;&nbsp;40&nbsp;)<br>&nbsp;&nbsp;&nbsp;时间阈值&nbsp;=&nbsp;40;</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 14px; margin-left: 0px; ">初始时间&nbsp;=&nbsp;GetTickCount();<br>while(&nbsp;消息队列中还有消息&nbsp;)<br>{<br>&nbsp;&nbsp;&nbsp;if(&nbsp;消息队列中的消息数量&nbsp;&gt;&nbsp;300&nbsp;)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一次性处理所有的消息;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从队列中取出第一条消息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;处理这条消息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当前时间&nbsp;=&nbsp;GetTickCount();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;经过时间&nbsp;=&nbsp;当前时间&nbsp;-&nbsp;初始时间;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;经过时间&nbsp;&gt;&nbsp;时间阈值&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp;这里增加了时间阈值这个静态变量，替代了之前代码中的20，使之成为一个由当前帧消息包数量决定的一个可变的值。当前帧消息包的数量超过一个值时，就将这个时间阈值加一，否则减一。这么做的效果就是，消息包来得越多，每帧用于处理消息的时间就越长，也就是说消息处理耗时的比重在逐渐上升。这样就能很大程度上降低消息数量超过上限的可能性。<br>最差的情况，如果这样做依然有周期性卡顿的话，这台机器真的就不适合运行这个游戏了，退一步讲，不作这个优化的话，这台机器玩这个游戏也依然会卡得要命。：）<br>&nbsp;&nbsp;&nbsp;当然，时间阈值的范围，和消息包的数量上限可以调整，以适合于不同的游戏。</p></span>
<img src ="http://www.cppblog.com/xmli/aggbug/104448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-12-30 14:00 <a href="http://www.cppblog.com/xmli/archive/2009/12/30/104448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>符号文件——Windows 应用程序调试必备</title><link>http://www.cppblog.com/xmli/archive/2009/09/15/96258.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Tue, 15 Sep 2009 10:26:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/09/15/96258.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/96258.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/09/15/96258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/96258.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/96258.html</trackback:ping><description><![CDATA[<br>
<p align="center"><strong>符号文件——Windows 应用程序调试必备</strong><br>
<br>
作者：Generad USam</p>
<p>　</p>
<p><strong>一、何谓符号文件？</strong></p>
<p>
符号文件（Symbol
Files）是一个数据信息文件，它包含了应用程序二进制文件（比如：EXE、DLL等）调试信息，专门用来作调试之用，最终生成的可执行文件在运行时并
不需要这个符号文件，但你的程序中所有的变量信息都记录在这个文件中。所以调试应用程序时，这个文件是非常重要的。用 Visual C++ 和
WinDbg 调试程序时都要用到这个文件。<br>
在 Windows 系统中，符号文件以 .pdb 为扩展名，比如：每个 Windows 操作系统下有一个 GDI32.dll
文件，编译器在编译该 DLL 的时候会产生一个 GDI32.pdb 文件，一旦你拥有了这个 PDB 文件，那么便可以用它来调试并跟踪到
GDI32.dll 内部。该文件和二进制文件的编译版本密切相关，比如修改了 DLL 的输出函数，再编译该 DLL，那么原先的 PDB
文件就过时了，不能再用老的 PDB 文件来做调试工作,而必须使用最新的 PDB 文件版本。<br>
Visual C++ 编译代码后会在 Debug 或者 Release 目录下生成一个 PDB
文件。一般情况下，符号文件包括以下的数据信息：</p>
<ol>
    <li>全局变量（Global variables）；</li>
    <li>局部变量（Local variables）；</li>
    <li>函数名和它们的入口地址（Function names and the addresses of their entry
    points）；</li>
    <li>FPO 数据（Frame Pointer Omission)：Frame Pointer 是一种用来在调用堆栈（Call
    stack）中找到下一个将要被调用的函数的数据结构源代码的行序号（Source-line numbers）；</li>
</ol>
<p><strong>二、如何得到和安装符号文件?</strong></p>
<ol>
    <li>先确定你的操作系统（OS）版本；</li>
    <li>到微软网站下载相应的符号文件；</li>
    <li>安装符号文件，对于符号文件的安装位置没有特贝要求，可以安装在任何目录中；</li>
    <li>设置环境变量，使得调试工具（比如：Visual C++、WinDbg、Ntsd、DrWatson 等）能找到符号文件；</li>
</ol>
<p>安装符号文件的注意事项：<br>
<br>
如果是手动安装符号文件，有一点很重要，那就是宿主机（Hostt Computer）上的符号文件必须与目标机器（Target
Computer）上的 Windows 版本相匹配。<br>
这里所谓的宿主机指的是运行调试会话的机器，在典型的双系统调试会话环境中，宿主机可以是连接到目标机器的任何机器。目标机器指的是发生软件组件、系
统服务、应用程序或操作系统运行失败的机器。也即是需要被调试的机器，它是调试会话关注的焦点。目标机器可以近在咫尺，也可以位于完全不同的地方。有时我
们也将目标机器称之为——被调试者（debuggee），那么与之对应，宿主机则可以称为调试者（debugger）。</p>
<p><strong>三、在 Visual C++ 使用符号文件的方法</strong></p>
<p>在 Visual C++ 6.0 中的使用方法：</p>
<ol>
    <li>打开 Visual C++ 6.0 的 Workspace 文件（*.dsw）；</li>
    <li>进入 Tools 菜单，选择 Options 菜单项 (Tools-&gt;Options)；</li>
    <li>单击 Directoties 标签；</li>
    <li>在 &#8220;Show directories for&#8221;下拉列表中选择 &#8220;Executable files&#8221;；</li>
    <li>将符号文件的路径添加到 &#8220;Directories&#8221; 路径列表中；</li>
    <li>单击&nbsp; OK 完成；</li>
</ol>
<p>在 Visual C++ .NET 2003 中的使用方法：</p>
<ol>
    <li>打开 Visual C++ .NET 的项目文件（*.vcproj）；</li>
    <li>在解决方案管理器中选中要使用符号文件的项目；</li>
    <li>单击右键进入项目属性对话框；</li>
    <li>选择&#8220;配置属性&#8221;中的&#8220;调试&#8221;；</li>
    <li>在与&#8220;调试&#8221;对应的&#8220;操作&#8221;选项中有一个&#8220;符号路径&#8221;，在此添加符号文件的路径即可；</li>
    <li>单击&nbsp; &#8220;确定&#8221; 完成；</li>
</ol>
<p><strong>四、如何产生 Release 版本二进制文件对应的 PDB 文件?</strong></p>
<p>在 Visual C++ 6.0 中的方法：</p>
<ol>
    <li>打开 Visual C++ 6.0 的 Workspace 文件（*.dsw）；</li>
    <li>进入 Project 菜单，选择 Settings 菜单项 (Project-&gt;Settings)，打开项目设置对话框；</li>
    <li>在 &#8220;Settings for&#8221;列表中选择项目的 Release 配置；</li>
    <li>单击&#8220;C/C++&#8221;标签；</li>
    <li>在&#8220;Category&#8221;下拉列表框中选择&#8220;General&#8221;选项；</li>
    <li>在&#8220;Debug info&#8221;下拉列表框中选择调试信息格式（具体选项参见图一），在此不必禁用任何优化选项；</li>
    <li>单击&#8220;Link&#8221;标签；</li>
    <li>在&#8220;Category&#8221;下拉列表框中选择&#8220;Debug&#8221;选项；</li>
    <li>选中&#8220;Debug info&#8221;复选框，然后选择需要的链接调试类型（具体选项参见图一）；</li>
    <li>不要选择&#8220;Separate types&#8221;复选框；</li>
    <li>在&#8220;Project options&#8221;编辑框的最后添加如下指令：/opt:ref,icf；</li>
    <li>重新生成（Rebuild）项目；</li>
</ol>
<p>在 Visual C++ .NET 2003 中的方法：</p>
<ol>
    <li>打开 Visual C++ .NET 的项目文件（*.vcproj）；</li>
    <li>进入 Project 菜单，选择 Settings 菜单项 (Project-&gt;Settings)，打开项目设置对话框；</li>
    <li>在 &#8220;配置&#8221;下拉列表中选择项目的 &#8220;（活动）Release&#8221; 配置；</li>
    <li>选择&#8220;配置属性&#8221;树型节点中的&#8220;C/C++&#8221; ==〉&#8220;常规&#8221;；</li>
    <li>设置右边的&#8220;调试信息格式&#8221;选项（具体选项参见图一）；</li>
    <li>选择&#8220;配置属性&#8221;树型节点中的&#8220;链接器&#8221;==〉&#8220;调试&#8221;；</li>
    <li>设置右边的&#8220;生成程序数据库文件&#8221;（具体选项参见图一）；</li>
    <li>选择&#8220;配置属性&#8221;树型节点中的&#8220;链接器&#8221;==〉&#8220;命令行&#8221;；</li>
    <li>在&#8220;附加选项(D)&#8221;编辑框中添加如下指令：/opt:ref,icf；</li>
    <li>按&#8220;确定&#8221;退出；</li>
    <li>重新生成（Rebuild）项目；</li>
</ol>
<img  src="http://www.cppblog.com/images/cppblog_com/xmli/SymbleFile.gif" border="0"><br>图一
<p><strong>五、关于 Free Build（也称 Retail Build）和 Checked Build（也称 Debug Build）</strong></p>
<p>每个基于 NT 操作系统有两种不同的程序生成模式，即：</p>
<ul>
    <li>Free Build (或 Retail Build)</li>
    <li>Checked Build (或 Debug Build)</li>
</ul>
<p>　　Free Build
生成的是最终用户版本，针对生成的二进制文件进行了彻底的优化，禁用了调试断言，并剥离了调试信息。这样一来使可执行程序文件更小，加载更快，使用的内存也更小。<br>
Checked Build 生成的是测试和调试版本。它包含额外的 Free Build 所没有的错误检查，参数验证和调试信息，Checked
Build 有助于隔离和跟踪可能导致不可预见的行为的问题，比如内存溢出，不正确的设备配置。虽然 Checked Build
提供了额外的保护，但与 Free Build
比较，它需要更多的内存开销和磁盘空间。由于可执行程序包含符号调试信息；调试时要执行附加的代码、参数检查和输出调试诊断信息，从而导致性能下降。<br>
<br>
<strong>六、系统符号文件的更新方法</strong><br>
<br>
系统符号文件指 Windows 操作系统依赖的那几个重要的 DLL/SYS
和可执行文件对应的符号文件，常见的比如：gdi32.dll、Kernel32.dll、Kerberos.dll、psapi.dll、user32.dll等，使用
WinDbg 调试时，你就会发现系统符号文件(PDB)有多重要，这些文件都与本地的 OS 密切相关，比如，Windows 2000
打了SP补丁的话，那么必须更新系统符号文件才能进行相关调试，原来的符号文件与打补丁后的系统就会不匹配，怎么办呢?
可以通过网络来更新！象下面这样在 WinDbg 的 Symbols Path 里面输入路径：</p>
<pre>SRV*<em><strong>D:\Symbols\websymbols*</strong></em>http://msdl.microsoft.com/download/symbols</pre>
（斜体部分是你在本地保存符号文件的路径）<br>
<br>
如果你不是通过代理上网，那么在你用 WinDbg 打开一个被调试程序后，输入 symchk 回车，WinDbg
就会自动的连到微软的网站根据你的机器的情况更新的 PDB
文件，并将它保存在上面斜体部分指定的本地路径里，这样你就可以确保你的符号文件版本和你机器上的文件版本一致。<br>
<br>
如果你是通过代理上网那么你需要配置 IE 的连接设置。具体方法恕不赘言。<br><br><img src ="http://www.cppblog.com/xmli/aggbug/96258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-09-15 18:26 <a href="http://www.cppblog.com/xmli/archive/2009/09/15/96258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>do...while(0)的妙用 </title><link>http://www.cppblog.com/xmli/archive/2009/09/03/95204.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Thu, 03 Sep 2009 06:51:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/09/03/95204.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/95204.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/09/03/95204.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/95204.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/95204.html</trackback:ping><description><![CDATA[在C++中，有三种类型的循环语句：for, while, 和do...while， 但是在一般应用中作循环时，
我们可能用for和while要多一些，do...while相对不受重视。<br>&nbsp;&nbsp;&nbsp;
但是，最近在读我们项目的代码时，却发现了do...while的一些十分聪明的用法，不是用来做循环，而是用作其他来提高代码的健壮性。
<p><strong>1.
do...while(0)消除goto语句。</strong><br>通常，如果在一个函数中开始要分配一些资源，然后在中途执行过程中如果遇到错误则退出函数，当然，退出前先释放资源，我们的代码可能是这样：<br><strong>version
1<br></strong></p>
<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous; width: 95%;">
<div><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;Execute()<br>{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;分配资源</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;bOk(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;执行并进行错误处理</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func1();<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func2();<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func3();<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;..........<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;执行成功，释放资源并返回</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;<br>}<br><br></span></div>
</div>
<p><br>这里一个最大的问题就是代码的冗余，而且我每增加一个操作，就需要做相应的错误处理，非常不灵活。于是我们想到了goto:<br><strong>version
2</strong><br></p>
<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous; width: 95%;">
<div><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;Execute()<br>{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;分配资源</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;bOk(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;执行并进行错误处理</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func1();<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;</span><span style="color: #0000ff;">goto</span><span style="color: #000000;">&nbsp;errorhandle;<br><br>&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func2();<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;</span><span style="color: #0000ff;">goto</span><span style="color: #000000;">&nbsp;errorhandle;<br><br>&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func3();<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;</span><span style="color: #0000ff;">goto</span><span style="color: #000000;">&nbsp;errorhandle;<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;..........<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;执行成功，释放资源并返回</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br><br>errorhandle:<br>&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;<br>}<br><br></span></div>
</div>
<p><br>代码冗余是消除了，但是我们引入了C++中身份比较微妙的goto语句，虽然正确的使用goto可以大大提高程序的灵活性与简洁性，但太灵活的东西往往是很危险的，它会让我们的程序捉摸不定，那么怎么才能避免使用goto语句，又能消除代码冗余呢，请看do...while(0)循环：<br><strong>version3<br></strong></p>
<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous; width: 95%;">
<div><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;Execute()<br>{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;分配资源</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;bOk(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">do</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;执行并进行错误处理</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func1();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func2();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bOk&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;func3();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #000000;">bOk)&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;..........</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;}</span><span style="color: #0000ff;">while</span><span style="color: #000000;">(</span><span style="color: #000000;">0</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;释放资源</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;p;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;bOk;<br>&nbsp;&nbsp;&nbsp;<br>}<br><br></span></div>
</div>
<p><br>&#8220;漂亮！&#8221;， 看代码就行了，啥都不用说了...</p>
<p><strong>2 宏定义中的do...while(0)</strong><br>&nbsp;
如果你是C++程序员，我有理由相信你用过，或者接触过，至少听说过MFC, 在MFC的afx.h文件里面，
你会发现很多宏定义都是用了do...while(0)或do...while(false)， 比如说：<br><span class="Code">#define
AFXASSUME(cond)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do { bool __afx_condVal=!!(cond); ASSERT(__afx_condVal);
__analysis_assume(__afx_condVal); } while(0)
</span><br>粗看我们就会觉得很奇怪，既然循环里面只执行了一次，我要这个看似多余的do...while(0)有什么意义呢？
<br>当然有！<br>为了看起来更清晰，这里用一个简单点的宏来演示：<br><span class="Code">#define SAFE_DELETE(p)
do{ delete p; p = NULL} while(0)<br></span>假设这里去掉do...while(0),<br><span class="Code">#define SAFE_DELETE(p) delete p; p = NULL;</span><br>那么以下代码：<br><span class="Code">if(NULL != p) SAFE_DELETE(p)<br>else&nbsp;&nbsp; ...do
sth...</span><br>就有两个问题，<br>1) 因为if分支后有两个语句，else分支没有对应的if，编译失败<br>2) 假设没有else,
SAFE_DELETE中的第二个语句无论if测试是否通过，会永远执行。<br>你可能发现，为了避免这两个问题，我不一定要用这个令人费解的do...while,&nbsp;
我直接用{}括起来就可以了<br><span class="Code">#define SAFE_DELETE(p) { delete p; p =
NULL;}</span><br>的确，这样的话上面的问题是不存在了，但是我想对于C++程序员来讲，在每个语句后面加分号是一种约定俗成的习惯，这样的话，以下代码:<br><span class="Code">if(NULL != p) SAFE_DELETE(p);<br>else&nbsp;&nbsp; ...do
sth...</span><br>其else分支就无法通过编译了（原因同上），所以采用do...while(0)是做好的选择了。</p>
<p>也许你会说，我们代码的习惯是在每个判断后面加上{}, 就不会有这种问题了，也就不需要do...while了，如：<br><span class="Code">if(...)
<br>{<br>}<br>else<br>{<br>}</span><br>诚然，这是一个好的，应该提倡的编程习惯，但一般这样的宏都是作为library的一部分出现的，而对于一个library的作者，他所要做的就是让其库具有通用性，强壮性，因此他不能有任何对库的使用者的假设，如其编码规范，技术水平等。 <br></p><img src ="http://www.cppblog.com/xmli/aggbug/95204.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-09-03 14:51 <a href="http://www.cppblog.com/xmli/archive/2009/09/03/95204.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何通过打印机之网络名称获取其IP地址</title><link>http://www.cppblog.com/xmli/archive/2009/09/02/95104.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 02 Sep 2009 07:51:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/09/02/95104.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/95104.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/09/02/95104.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/95104.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/95104.html</trackback:ping><description><![CDATA[&nbsp; 前天有大学同学问到这个问题，据说寻觅了好久：<br>&nbsp; 1、如何根据网络打印机的名字，获取该打印机的IP；<br>&nbsp; 2、如何从shd文件获取打印相关的信息；<br>&nbsp; <br>&nbsp; 问题一与&#8220;查找网络上服务器的ip地址&#8221;是相同的问题，比如查找g.cn的ip地址。以下假定网络打印机名称为"\\printer"，则可通过以下代码获取其ip地址。<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">iostream</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">WinSock2.h</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>#pragma&nbsp;comment(lib,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Ws2_32</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br><br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;argc,&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;WSADATA&nbsp;wsaData;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;err_code</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;WSAStartup(MAKEWORD(</span><span style="color: #000000;">2</span><span style="color: #000000;">,</span><span style="color: #000000;">2</span><span style="color: #000000;">),</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">wsaData);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">0</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;err_code)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hostent</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;pHost</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;gethostbyname(</span><span style="color: #000000;">"</span><span style="color: #000000;">printer</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(NULL</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;pHost)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in&nbsp;&nbsp;&nbsp;sa;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;pHost</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">h_addr_list[i];&nbsp;</span><span style="color: #000000;">++</span><span style="color: #000000;">i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">sa.sin_addr.s_addr,&nbsp;pHost</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">h_addr_list[i],&nbsp;pHost</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">h_length);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">"</span><span style="color: #000000;">IP=&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">&nbsp;inet_ntoa(sa.sin_addr)</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">&nbsp;std::endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;system(</span><span style="color: #000000;">"</span><span style="color: #000000;">pause</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>}</span></div>
<br>&nbsp;&nbsp; 问题二尚无头绪，借宝地问一下各位前辈有没有知道解决方案的？谢谢啦！<br><br><br><br><br><img src ="http://www.cppblog.com/xmli/aggbug/95104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-09-02 15:51 <a href="http://www.cppblog.com/xmli/archive/2009/09/02/95104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC6中的简易delegate实现</title><link>http://www.cppblog.com/xmli/archive/2009/09/01/94977.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Tue, 01 Sep 2009 06:47:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/09/01/94977.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/94977.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/09/01/94977.html#Feedback</comments><slash:comments>18</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/94977.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/94977.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: VC6中的简易delegate实现版本：0.1最后修改：2009-09-01撰写：李现民	其实原本不想自己写C++委托类的，因为CodeProject已经有许多相关讨论了，国内的前辈也写了很多，但经过一一试用后我无奈的发现它们无一例外的都使用了大量的template技巧，而一些像偏特化之类的template特性在VC6中并没有得到支持。于是只好自己动手了，虽然粗陋，但好在还...&nbsp;&nbsp;<a href='http://www.cppblog.com/xmli/archive/2009/09/01/94977.html'>阅读全文</a><img src ="http://www.cppblog.com/xmli/aggbug/94977.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-09-01 14:47 <a href="http://www.cppblog.com/xmli/archive/2009/09/01/94977.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>get与set成员函数是为代码耦合之重要原因</title><link>http://www.cppblog.com/xmli/archive/2009/08/21/94006.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 21 Aug 2009 03:20:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/21/94006.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/94006.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/21/94006.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/94006.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/94006.html</trackback:ping><description><![CDATA[<meta http-equiv="CONTENT-TYPE" content="text/html; charset="utf-8"">
<title></title>
<meta name="GENERATOR" content="OpenOffice.org 3.1  (Win32)"><style type="text/css">
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style>
<p style="margin-bottom: 0cm;" align="CENTER"><font color="#000000"><font face="Times New Roman, serif"><font style="font-size: 16pt;" size="4"><strong>get</strong></font></font><font style="font-size: 16pt;" size="4"><strong>与</strong></font><font face="Times New Roman, serif"><font style="font-size: 16pt;" size="4"><strong>set</strong></font></font><font style="font-size: 16pt;" size="4"><strong>成员函数是为代码耦合之重要原因</strong></font></font></p>
<p style="margin-bottom: 0cm;" align="CENTER">版本：<font face="Times New Roman, serif">0.1</font></p>
<p style="margin-bottom: 0cm;" align="CENTER">最后修改：<font face="Times New Roman, serif">2009-08-21</font></p>
<p style="margin-bottom: 0cm;" align="CENTER">撰写：李现民</p>
<p style="margin-bottom: 0cm;" align="CENTER" lang="zh-CN"><br>
</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	</font>类数据成员的访问级别通常需定义为<font face="Times New Roman, serif">private</font>，以封装类的实现细节，这样可以在类的生命演化过程中提供更好实现弹性。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	get/set</font>成员函数（访问级别通常为<font face="Times New Roman, serif">public</font>）使得<font face="Times New Roman, serif">client</font>端用户代码可以访问对象的内部数据结构，这会暴露类内部的实现细节。这种暴露使会得用户代码与类实现之间产生深层次的依赖关系，而这种过剩的知识将在类实现技术改变时迅速破坏相关的用户代码<font face="Times New Roman, serif">---</font>涟漪效果。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	</font>比如<font face="Times New Roman, serif">Container</font>类输出了关于实现该类之二叉树的信息（比如，当它输出成员函数<font face="Times New Roman, serif">getLeftChild()</font>与<font face="Times New Roman, serif">getRightChild()</font>时），用户将被迫按照二叉树而不是容器进行思考，这将使用户代码变得复杂且难以改变。如果<font face="Times New Roman, serif">Container</font>类改变了实现结构，则用户代码将被迫进行修改（可能是大量的）。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	</font>最少知识（<font face="Times New Roman, serif">least
knowledge</font>）原则是用于面向对象编码中降低类间耦合度的指导原则。该原则认为如果要在相互调用的类（对象）之间保持较低的耦合度，则一个对象所调用的方法应该仅仅局限于以下几个来源：</p>
<ol>
    <li>
    <p style="margin-bottom: 0cm;" lang="zh-CN">类对象本身；</p>
    </li>
    <li>
    <p style="margin-bottom: 0cm;" lang="zh-CN">被当作方法的参数而传递进来的对象；</p>
    </li>
    <li>
    <p style="margin-bottom: 0cm;" lang="zh-CN">此方法所创建或实例化的任何对象；</p>
    </li>
    <li>
    <p style="margin-bottom: 0cm;" lang="zh-CN">对象的任何组件；</p>
    </li>
</ol>
<p style="margin-bottom: 0cm;" lang="zh-CN"><br>
</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	</font>一个对象可以任意调用以上四类对象的方法。唯一一类不可调用的方法来源于：通过某个对象的<font face="Times New Roman, serif">get</font>成员函数所获取的间接对象的成员函数。很容易想象，当某个类拥有大量<font face="Times New Roman, serif">get/set</font>成员函数时，该类本身几乎不可能提供完善的逻辑处理方法（否则也就没有必要提供这些<font face="Times New Roman, serif">get/set</font>成员函数了），因此借助<font face="Times New Roman, serif">get</font>成员函数获取间接对象并做进一步的处理几乎是不可避免的。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	</font>因此，在有可能的情况下，类设计人员应该尽量不提供<font face="Times New Roman, serif">get</font>与<font face="Times New Roman, serif">set</font>成员函数。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><font face="Times New Roman, serif">	</font>当然，不要认为<font face="Times New Roman, serif">get</font>与<font face="Times New Roman, serif">set</font>成员函数总是坏的，像<font face="Times New Roman, serif">CORBA</font>这样的框架都会为所有的属性自动提供<font face="Times New Roman, serif">get/set</font>成员函数。真正的问题是：好的对象总会封装并在接口后面隐藏某些东西，然而<font face="Times New Roman, serif">get/set</font>成员函数有时会在暗中暴露对象的秘密。只有当在类外（从用户的角度）看待这些私有数据仍&#8220;有意义&#8221;时，为私有数据设置公有的<font face="Times New Roman, serif">get()</font>和<font face="Times New Roman, serif">set()</font>成员函数才是合理的。然而在许多情况下，
<font face="Times New Roman, serif">get()/set()</font>成员函数和公有数据一样差劲：它们仅仅隐藏了私有数据的名称，而没有隐藏私有数据本身。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN"><br>
</p>
<p style="margin-bottom: 0cm;" lang="zh-CN">注<font face="Times New Roman, serif">1</font>：以上文字部分参考了《<font face="Times New Roman, serif">C++
FAQs</font>》<font face="Times New Roman, serif">second edition,
P73</font>的内容。</p>
<p style="margin-bottom: 0cm;" lang="zh-CN">注<font face="Times New Roman, serif">2</font>：我们经常使用<font face="Times New Roman, serif">get/set</font>成员函数作为急救带来修补蹩脚的接口。</p><img src ="http://www.cppblog.com/xmli/aggbug/94006.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-21 11:20 <a href="http://www.cppblog.com/xmli/archive/2009/08/21/94006.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之九：次序问题</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93805.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 04:03:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93805.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93805.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93805.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93805.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93805.html</trackback:ping><description><![CDATA[&#8220;zero 帮帮忙吧 ～～ &#8221; <br><br>听到这个充满诚意的声音，zero 垮下了双肩，感觉自己处于彻底崩溃的边缘 ———— 几个月来，每次 pisces 遇到什么不能解决的问题，总是用这个开场白来求 zero，而后 zero 就不得不面对各式各样匪夷所思的古怪问题。 <br><br>&#8220;她怎么就能弄出那么多错误来呢？&#8221; zero 在心中哀叹。他苦着脸，问道：&#8220;你就不能放过我去找 Solmyr 么？&#8221; <br><br>&#8220;可是 Solmyr 指定你来帮助我们的呀！&#8221; <br><br>&#8220; &#8230;&#8230; 好吧，说说是什么问题&#8221;，zero 一边在心里再次痛骂了 Solmyr 一百遍，一边做好了再次面对奇怪错误的准备。 <br><br>&#8220;嗯，这里有一段代码，是读取配置文件信息的。现在只是个框架，将来可能需要读好些配置文件，而且可能放在不同目录下，所以这里我用一些 string 对象保存路径和文件名，然后用 fopen 打开。&#8221; 说着，pisces 调出了一个 cpp 文件： <br><br>#include &lt;cstdio&gt; <br>#include &lt;string&gt; <br>#include "config.h" &nbsp;&nbsp;&nbsp;// 头文件，定义了存放配置信息的结构 <br>&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;// 并包含 read_cfg 函数的声明 <br><br>using namespace std; <br><br>const string path = "./cfg/"; <br>const string name = "system.cfg"; <br><br>// 参数为指向保存配置信息结构的指针 <br>// 返回值为成功标志，true 代表成功，false 代表失败 <br>bool read_cfg(CFG_DATA* p_data) <br>{ <br>&nbsp;&nbsp;&nbsp;const string path_name = path+name; <br>&nbsp;&nbsp;&nbsp;FILE* fp = fopen(path_name.c_str(), "r"); <br>&nbsp;&nbsp;&nbsp;if( fp == NULL ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false; <br><br>&nbsp;&nbsp;&nbsp;// 使用 fp 读取配置文件，放入 CFG_DATA 结构 <br>&nbsp;&nbsp;&nbsp;... ... <br>&nbsp;&nbsp;&nbsp;... ... <br><br>&nbsp;&nbsp;&nbsp;return true; <br>} <br><br>... .... <br><br>&#8220;在我这里运行的好好的，但提交给测试组做单元测试的时候却总是出毛病，说是找不到配置文件，我去他们那里看过了，配置文件的名字和路径都是对的呀，真是太奇怪了。&#8221; <br><br>zero
斜着眼看了看这段代码，对其中的风格大为不满：&#8220;我说 pisces，错误先不提，你这段代码的风格实在不太象 C++
啊。首先参数应该用引用，它的安全性可比指针高多了，而且你既然用了指针，就应该用断言做检查么；其次你干吗不用流来读取文件呢？非要用 fopen
&#8230;&#8230;&#8221; <br><br>&#8220;我知道我知道！&#8221; pisces 忙不迭的打断了
zero，&#8220;我知道这里我的风格不太好，可是无论怎样这段打开文件的代码应该没错呀？可现在问题是打开文件的时候出错，明明文件在那儿的，可函数总是返回
false 。帮我先把这个 bug 找出来吗，帮帮忙了 ～～～～～ &#8221; <br><br>&#8220;好好！&#8221;，zero 摇了摇头，开始寻找错误。5
分钟过去，zero 的眉头渐渐皱了起来，这段只有三五行的代码看起来一目了然，根本没有隐藏错误的地方。zero
把这段代码引进他用来测试的工程里，编译连接，测试运行，程序一切正常，正确的找到了配置文件。这是怎么回事？zero 迷惑了。 <br><br>&#8220;zero？&#8221;
pisces 将探询的眼光投向 zero。&#8220;呃 &#8230;&#8230; 别急，我们去测试组那里看看究竟怎么回事。&#8221; zero
心存侥幸，没准是测试组弄错了呢？10 分钟之后，zero 垂头丧气的回来了，后面跟着
pisces。测试组没有弄错，在他们那里这段代码确实不能正常工作，调试器显示，作为文件名传入 fopen
的是个空字符串。这是怎么回事？zero 一边想着，一边往自己的座位走去 ———— 哎？那个站在自己计算机前的人不是 &#8230;&#8230; 不是 &#8230;&#8230;
Solmyr 么？他手上拿的是 &#8230;&#8230; <br><br>zero 本能的感到了危险，猛的一偏头！一个文件夹从离他的脸只有 0.01 公分的地方唰的一下擦了过去！可惜后面的 pisces 就没有这么幸运了，被打个正着！ <br><br>Solmyr 看了一眼正捂着脸的 pisces，对吓出一身冷汗的 zero 问道：&#8220;屏幕上那段代码不是你写的吧？&#8221; <br><br>&#8220;对，是她写的。&#8221; zero 同情的看了看 pisces ，后者好象还没有从打击中恢复过来 &#8230;&#8230; <br><br>&#8220;有因就有果，真是一点也不会错啊 &#8230;&#8230;&#8221; Solmyr 耸耸肩，&#8220;知道这段代码为什么会出错么？&#8221; <br><br>zero 苦笑：&#8220;不知道。&#8221; <br><br>&#8220;把眼光放开一点，你看一下调用这个函数的地方就知道了。&#8221; <br><br>zero 调出了整个工程。按照文档上的说明，read_cfg 是整个系统初始化过程的步骤之一，当系统启动时会读取配置文件确定一些初始化的参数。据此，zero 很容易的找到了调用处，在另外一个 cpp 文件中： <br><br>#include "config.h" &nbsp;&nbsp;&nbsp;// 其中声明了 read_cfg 这个函数 <br><br>class system <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp;// 完成系统启动时的初始化工作 <br>&nbsp;&nbsp;&nbsp;system() <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CFG_DATA data; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;read_cfg(&amp;data); <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 使用 data 中的信息配置系统 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;... ... <br>&nbsp;&nbsp;&nbsp;} <br><br>&nbsp;&nbsp;&nbsp;// 完成系统退出时的清理工作 <br>&nbsp;&nbsp;&nbsp;~system() <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;... ... <br>&nbsp;&nbsp;&nbsp;} <br><br>&nbsp;&nbsp;&nbsp;... ... <br>}; <br><br>system theSystem; &nbsp;&nbsp;&nbsp;// 代表整个系统的全局对象 <br><br>Solmyr
清了清嗓子：&#8220;这是个看起来很干净的手法。system
这个类只有这里一个全局对象，这个全局对象代表了整个系统，它构造，系统做初始化工作；它析构，系统开始做退出时的清理工作。全局对象的身份保证了它会在
进入 main 函数之前构造，在 main 函数退出之后析构。这一招是你教 pisces 的吧？&#8221;Solmyr 看了看 zero 。 <br><br>zero 点点头：&#8220;没错，上次她问我如何能够比较好的处理初始化和清理的代码，我想起了上次关于&#8216;成对出现 &#8217;的讨论（注一），就给她出了这个主意。&#8221; <br><br>&#8220;思路是对头的，但是实现的方式不妥，毛病就出在全局对象上面。我问你，一个 cpp 文件，或者说得正式一点，一个编译单元中的全局对象构造析构的顺序是怎样的？&#8221; <br><br>&#8220; &#8230;&#8230; 应该是按照定义它们的顺序。&#8221; zero 努力的回忆一阵，很肯定的说道。 <br><br>&#8220;正确，那么不同编译单元之间全局对象的构造顺序呢？&#8221; <br><br>&#8220; &#8230;&#8230; 好象没有明确的规则，这个应该属于标准未定义对吧？&#8221; <br><br>&#8220;正确，所以 &#8230;&#8230;&#8221; <br><br>&#8220;所
以 &#8230;&#8230; &#8230;&#8230; 啊！我明白了！哎呀！我怎么这么迟钝！看到 fopen 传入的是空字符串的时候我就应该想到的！&#8221; zero
露出了恍然大悟的表情，&#8220;在打开配置文件的代码段中，保存文件名的 path 和 name 也是全局对象，换句话说，这两个 string 对象和
theSystem 对象的构造次序是无法确定的。在测试组那里，theSystem 先于 path 和 name 构造，所以当
theSystem 的构造函数调用 read_cfg 函数的时候，path 和 name 这两个 string
对象根本还没有来得及构造！当然无法取出文件名和路径来！而在我和 pisces 的计算机上，构造次序与之相反，这段代码就可以正确运行。&#8221; <br><br>&#8220;很好，那么如何解决呢？&#8221; <br><br>&#8220;嗯
&#8230;&#8230; &#8230;&#8230; 我想只要尽可能避开全局对象就行了，一方面 theSystem 这个对象可以放到 main
函数里，一样可以保证正确完成初始化工作和清理工作；另一方面，read_cfg 那边最好也不要用全局的 string
对象了，一样可以用局部对象。这样是能够解决这个问题 &#8230;&#8230;&#8221; zero
皱起了眉头，显然对这个解法还不太满意，&#8220;那如果我有一些全局性质的对象，而且希望精确的定义它们的构造次序，该怎么办呢？&#8221; <br><br>Solmyr 点了点头：&#8220;没错，有时候这确实是个合理的要求。对此最简单的解法是&#8216;被函数包装的 static 对象&#8217;（注二），象这样：&#8221; <br><br>system&amp; theSystem() <br>{ <br>&nbsp;&nbsp;&nbsp;static system instance; <br>&nbsp;&nbsp;&nbsp;return instance; <br>} <br><br>&#8220;instance 是个 static 对象，这保证了它的生存期，然后它会在第一次调用这个函数的时候构造。针对你的问题，只要你声明多个这样的函数，然后保证它们第一次调用的次序，就可以保证这些对象构造的次序了。&#8221; <br><br>zero 若有所思的点了点头。 <br><br>&#8220;说
起来，次序问题绝对不只这一个，C++
中类似的问题相当多。和这个问题最接近的，是类的成员和基类的构造次序，其他的还有表达式求值的次序、函数参数求值的次序，（注三）等等。遇到次序问题，
千万不要想当然，问自己一句：这个次序有定义吗？有定义的要遵守，无定义的要避开。好了，这个问题大概就是这样，接下来你的任务是 &#8230;&#8230; &#8221; <br><br>&#8220;我知道，把这些讨论整理成文档对吧？&#8221; <br><br>&#8220;不，是想办法让 pisces 搞懂这个问题。&#8221; <br><br>&#8220;&#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230;&#8221; <br><br>望着 Solmyr 甩手匆匆离去的背影，再看看身边正用最拿手的&#8220;诚恳&#8221;眼神看着自己的 pisces ，zero 突然泛起了一种奇怪的感觉：好象以前只是单纯被 Solmyr 砸的日子也没有那么糟糕 &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; <img src ="http://www.cppblog.com/xmli/aggbug/93805.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 12:03 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93805.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之八：拷贝</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93800.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 03:38:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93800.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93800.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93800.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93800.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93800.html</trackback:ping><description><![CDATA[&#8220;zero 帮帮忙吧 ～～ &#8221; <br><br>&#8220;灿烂&#8221;的笑脸，充满诚意的眼神，再加上点头哈腰的姿势，这三者构成了一尊名为&#8220;有求于人&#8221;的塑像。 <br><br>在 QQ 上聊的正欢的 zero 抬起头，看着塑像的作者和材料 ——— pisces ，方圆五十米内唯一的女性程序员 ——— 问道：&#8220;什么事？&#8221; <br><br>&#8220;我这里有一段 C++ 程序调不通。&#8221; <br><br>&#8220;这类问题你应该去问 Solmyr。&#8221; <br><br>&#8220;哎呀，别开玩笑了，我哪敢去问他呀！总说我笨！上次问他一个小问题，结果又被训的狗血喷头，哼！&#8221;，pisces 显得忿忿不平，&#8220;还是你来帮帮我吧，我知道你是部门里有数的高手，肯定搞的定的。帮帮忙吧 ～～&#8221; <br><br>zero 明显的被打动了，于是，在 pisces 的努力下，zero 坐到了 pisces 的计算机前。 <br><br>&#8220;好吧，什么问题？&#8221; <br><br>&#8220;是这样的啦，这里有一组 C 风格的 API ，负责管理设备上的字符通信链接。它们是好些年前设计的&#8221;，说着，pisces 调出了一些代码： <br><br>// old C style API <br>typedef int conn_handle; <br>typedef struct <br>{ <br>&nbsp;&nbsp;&nbsp;/* ... 打开链接所需的参数和属性 ... */ <br>}conn_attr; <br><br>conn_handle open_conn(conn_attr* p_attr, char* buf, unsigned int buf_size); <br>void close_conn(conn_handle h); <br><br>char read_conn(conn_handle h); <br>void write_conn(conn_handle h, char c); <br><br>... <br><br>&#8220;枝节的东西不算，主干大概就是这样，一对函数负责打开和关闭，一对函数负责读写。创建链接时候的那个 buf
参数指向一个缓冲区，这个要你自己分配并把长度传进去，和链接一一对应，read_conn/write_conn
会用它做缓冲。我的任务就是写个类把这些 API 包装起来。&#8221;，说着 pisces 又调出了另外一段代码： <br><br>// pisces' connection class <br>class connection <br>{ <br>private: <br>&nbsp;&nbsp;&nbsp;conn_attr m_attr; <br>&nbsp;&nbsp;&nbsp;bool m_opened; <br>&nbsp;&nbsp;&nbsp;int m_bufsize; <br>&nbsp;&nbsp;&nbsp;char* m_buf; <br><br>&nbsp;&nbsp;&nbsp;conn_handle m_h; <br>&nbsp;&nbsp;&nbsp;... <br><br>public: <br>&nbsp;&nbsp;&nbsp;connection(const conn_attr&amp; attr, int bufsize) <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_attr = attr; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_opened = false; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_bufsize = bufsize; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_buf = new char[m_bufsize]; <br>&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;~connection() { delete m_buf; } <br><br>&nbsp;&nbsp;&nbsp;void open() <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_h = open_conn(&amp;m_attr, m_buf, m_bufsize); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_opened = true; <br>&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;void close() <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close_conn(m_h); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_opened = false; <br>&nbsp;&nbsp;&nbsp;} <br><br>&nbsp;&nbsp;&nbsp;char read() <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(m_opened); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return read_conn(m_h); <br>&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;void write(char c) <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(m_opened); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;write_conn(m_h, c); <br>&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;... <br><br>}; <br><br>&#8220;应该是很简单的，可是不知道怎么回事，用了 connection 类的程序总是时不时的崩溃，说是非法的内存操作。&#8221;，pisces 显得很苦恼。 <br><br>zero 一眼就看出了毛病 ——— 这使他小小的自鸣得意了一下 ——— 但是表面上不动声色，等到他看过 pisces 提供的&#8220;总是引发崩溃&#8221;的代码段之后，他才开口说到： <br><br>&#8220;这
是一个常见的错误 pisces&#8221;，zero 尽量使自己的口吻和语气听起来象一个权威，&#8220;关于
C++，有一条重要的指导原则：析构函数、拷贝构造函数和赋值运算符三者几乎总是一起出现。也就是说，如果你为一个类写了析构函数，那么往往你不得不再提
供一个拷贝构造函数和一个赋值运算符，违反它往往意味着错误。你看这里：&#8221; <br><br>说着，zero 在屏幕上标出了两行代码： <br><br>void some_func() <br>{ <br>&nbsp;&nbsp;&nbsp;conn_attr attr; <br>&nbsp;&nbsp;&nbsp;... <br>&nbsp;&nbsp;&nbsp;connection c1(512, attr); <br>&nbsp;&nbsp;&nbsp;connection tmp = c1; <br>&nbsp;&nbsp;&nbsp;... <br>} <br><br>&#8220;这
里对象 tmp 是从 c1 拷贝构造而来的，而你没有定义拷贝构造函数，这使得编译器在这里自动进行按位拷贝，而这使得 tmp 和 c1
的所有成员都相等，包括 m_buf 成员。这样在函数返回时，c1 析构的时候 delete 了一遍 m_buf，在 tmp 析构的时候又
delete 了一遍 &#8230;&#8230;&#8221; <br><br>&#8220;哦！我明白了！&#8221; pisces 打断了 zero ，&#8220;所以就出现一个非法内存操作，对吧？哎呀，这一条以前在学校里写 string 类的时候遇到过，我怎么会忘了呢？&#8221; <br><br>&#8220;对，你只要写一个拷贝构造函数和一个赋值运算符处理一下 m_buf 指针就可以解决这个问题了。这你自己搞的定吧？&#8221; <br><br>&#8220;我可以的，多谢了 zero ！&#8221; <br><br>zero 心满意足的回到了自己的座位上，开始继续和&#8220;你不懂我纤细的心&#8221;在 QQ 上探讨&#8220;爱情的意义&#8221;。可是好景不长，没过多久，本文开头所描述的景象再一次的出现了。 <br><br>&#8220;zero 帮帮忙吧 ～～ &#8221; <br><br>zero 在心中叹了口气，抬头问道：&#8220;又是什么问题，pisces？&#8221; <br><br>&#8220;呃，还是那个类。我照你说的给 conn 添加了拷贝构造函数，非法内存操作确实少多了，可还是有，还有好像链接传输数据也有点问题 ———— 你还是过来帮我看看吧 ～～&#8221; <br><br>zero 心不甘情不愿的再次来到了 pisces 的计算机前，翻出 pisces 写的拷贝构造函数检查起来： <br><br>connection(const connection&amp; other) <br>{ <br>&nbsp;&nbsp;&nbsp;m_attr = other.m_attr; <br>&nbsp;&nbsp;&nbsp;m_bufsize = other.m_bufsize; <br>&nbsp;&nbsp;&nbsp;m_buf = new char[m_bufsize]; <br>&nbsp;&nbsp;&nbsp;memcpy(m_buf, other.m_buf, m_bufsize); <br>&nbsp;&nbsp;&nbsp;m_opened = other.m_opened; <br><br>&nbsp;&nbsp;&nbsp;m_h = other.m_h; <br>} <br><br>zero 的眉头皱了起来，这个拷贝构造函数似乎应该可以解决问题，显然现在两个 m_buf 各自指向合法的内存，不再存在两次释放的问题。那么问题出在哪儿呢？zero 陷入了沉思。不过仅仅多花了不到 1 分钟，zero 就明白了过来。 <br><br>&#8220;哦！我明白了！见鬼，我怎么会没注意到这个。pisces ，问题还是出在 m_buf 上面，因为链接和缓冲区指针是一一对应的，所以拷贝构造函数里新分配的缓冲区根本不起作用。&#8221; <br><br>pisces 眨了眨眼，表情略显呆滞。 <br><br>&#8220;给你举个例子吧。&#8221;zero 飞快的键入一段测试代码： <br><br>connection* pc = NULL; <br><br>{ <br>&nbsp;&nbsp;&nbsp;conn_attr attr; <br>&nbsp;&nbsp;&nbsp;connection c1(512, attr); <br>&nbsp;&nbsp;&nbsp;c1.open(); <br>&nbsp;&nbsp;&nbsp;pc = new connection(c1); <br>} <br><br>pc-&gt;write('A'); <br><br>&#8220;c1
的构造函数里调用 new 为它的 m_buf 成员分配内存，紧接着在 open 函数里调用 open_conn
打开了一个链接，注意这里我们传入 open_conn 的参数是 c1.m_buf ，所以这个链接对应的缓冲区指针是 c1.m_buf
。然后我们执行 pc = new connection(c1)，新对象从 c1 拷贝构造，所以 pc-&gt;m_h 和 c1.m_h
相等，也就是说这两个对象保存的 m_h 标识着同一个链接，对应的缓冲区指针都是 c1.m_buf ———— &#8221; <br><br>zero 象 Solmyr 常做的那样停了下来，但却失望的看到 pisces 毫无反应，只好接着往下说： <br><br>&#8220;所
以接下来的 pc-&gt;write 在调用 write_conn 时候，这个 API 并不知道这是通过另外一个对象在调用它，它仍然试图使用
c1.m_buf 作为缓冲区，但这个时候 c1 已经结束了它的生命周期，c1.m_buf 已经被释放了，所以，这是一个非法的内存访问。&#8221; <br><br>pisces 舔了舔嘴唇：&#8220; &#8230;&#8230; 那 &#8230;&#8230; 那么现在怎么办？&#8221; <br><br>zero 翻了个白眼 ——— 很明显 pisces 根本没明白是怎么一回事 ——— 开始考虑怎样应付眼前这个问题。 <br><br>&#8220;嗯，
看样子，这里必须考虑多个对象共享一个指针的问题，嗯，为了保证这块内存被释放 &#8230;&#8230; 恐怕 &#8230;&#8230;
恐怕得用上引用计数技术（请参见&#8220;小品文系列之五：垃圾收集&#8221;）才搞得定，要不要用 boost::shared_ptr 呢？&#8221;，zero
一边想，一边自言自语。突然间 ——— <br><br>&#8220;逻辑的混乱导致实现上的复杂，zero，这个 connection 类千疮百孔啊。&#8221;，Solmyr 的声音毫无预兆的在背后响起。 <br><br>zero 在 0.01 秒内控制住了拔腿飞奔的冲动，以尽可能放松的姿态缓缓的转过身来。在他的面前是披着一贯优雅伪装的 Solmyr，一手端着果汁，一手牢牢的拽着仍在拼命挣扎试图逃走的 pisces 。 <br><br>&#8220;啊 Solmyr ，我正想找你呢，这个问题稍许有点棘手。&#8221; <br><br>&#8220;是吗？那你的腿为什么在抖？&#8221; <br><br>&#8220;嗯？没有，有点冷而已 &#8230;&#8230; 啊 Solmyr ，你刚刚说什么来着？&#8221; <br><br>&#8220;逻辑的混乱导致实现上的复杂，zero，这个 connection 类千疮百孔。&#8221;Solmyr 把 pisces 按在旁边的座位上，接着说到：&#8220;你刚才发现的问题只是其中之一而已。看一下这个：&#8221; <br><br>void some_func() <br>{ <br>&nbsp;&nbsp;&nbsp;conn_attr attr; <br>&nbsp;&nbsp;&nbsp;... <br>&nbsp;&nbsp;&nbsp;connection c1(512, attr); <br>&nbsp;&nbsp;&nbsp;c1.open(); <br>&nbsp;&nbsp;&nbsp;... <br>&nbsp;&nbsp;&nbsp;connection tmp = c1; <br>&nbsp;&nbsp;&nbsp;c1.close(); <br>&nbsp;&nbsp;&nbsp;tmp.write('a'); <br>&nbsp;&nbsp;&nbsp;... <br>} <br><br>&#8220;这会导致什么？&#8221; <br><br>&#8220; &#8230;&#8230; 试图写入一个已经关闭了链接。&#8221; <br><br>&#8220;还需要我给出多次打开一个链接，多次关闭一个链接，以及各种链接处于打开状态但读写却会引发断言错误的例子吗？&#8221; <br><br>&#8220; &#8230;&#8230; 不用了。&#8221; <br><br>&#8220;那你打算怎样修复这些问题？要不要在每个对象里保存一个由它拷贝构造而来的对象列表？或者你打算在文档里写&#8216;以下 371 种方式使用该类会导致无法预知的错误&#8217;？&#8221; <br><br>&#8220; &#8230;&#8230; &#8221; <br><br>Solmyr
重重的叹了口气：&#8220;你被 pisces 误导了，zero，因为你只想着怎么帮 pisces
解决问题，如果一开始就让你来设计这个类，情况一定不会这么糟糕。&#8221;说着，Solmyr 狠狠的瞪了 pisces 一眼。&#8220;不要忘了，C++
类不是简单的把一堆成员变量和成员函数凑在一起，永远记得这个原则：C++ 中用类来表示概念。&#8221; <br><br>zero 点了点头。 <br><br>&#8220;我来问你，connection 这个类应该表示什么概念？&#8221; <br><br>&#8220;呃，应该表示&#8216;链接&#8217;这个概念。&#8221; <br><br>&#8220;一个 connection 类的对象应该代表 &#8230;&#8230;&#8221; <br><br>&#8220;应该代表一个实际&#8216;链接&#8217;。&#8221; <br><br>&#8220;很好。那么你告诉我，你刚才努力想设计出的那个拷贝构造函数要干什么？&#8221; <br><br>&#8220; &#8230;&#8230; 让两个 connection 对象能够表示同一链接。&#8221; <br><br>&#8220;所以 &#8230;&#8230;&#8221; <br><br>&#8220;
&#8230;&#8230; 所以 &#8230;&#8230; 嗯 &#8230;&#8230; 哦 &#8230;&#8230; &#8221;zero 露出了恍然大悟的表情：&#8220;所以我实际上想做的是要表达这样一个概念：如果一个
connection
对象没有被拷贝，它就表示一个独立的链接，如果它被拷贝了，那么它就和拷贝者表示同一个链接，这也包括拷贝者的拷贝者，拷贝者的拷贝者的拷贝者 &#8230;&#8230;
天哪，这根本是一团乱麻！&#8221; <br><br>&#8220;对，问题就在这里。一个 connection
对象代表什么？你试图给出一个在逻辑上非常混乱的答案，这导致了实现的复杂性。实际上，如果理清这个逻辑，问题是很简单的：一个 connection
对象代表一个链接，它构造，代表建立了一个链接；它析构，代表这个链接走完了它的生命历程 ——— 这里 open 和 close
这两个成员函数根本就是多余的。至于拷贝构造 &#8230;&#8230;&#8221; <br><br>Solmyr 顿了顿，以一种斩钉截铁式的语气说到： <br><br>&#8220;应该禁止。&#8221; <br><br>&#8220;禁止拷贝？！&#8221; <br><br>&#8220;对，
应该禁止。事实上，对于&#8216;链接&#8217;这个概念而言，&#8216;拷贝&#8217;动作含义模糊：拷贝意味着什么？拷贝构造的对象所表示的链接和原来的链接是什么关系？当使用
connection 类的程序员看到 connection c2 = c1; 这样的代码时，他没法从代码本身看出这是什么意思，他会猜测，c1
和 c2 代表的是一个链接？还是两个链接？只能通过查阅文档来解决，这加重了使用者的负担，而如果禁止拷贝，所有智力正常的程序员都会明白每个
connection 对象唯一的代表一个链接。&#8221; <br><br>zero 若有所思的点了点头。 <br><br>&#8220;同时，这还能阻止程序员用传值方式向函数传递 connection 对象 ——— 想象一下，如果一个程序员这样使用 connection ，会发生什么？&#8221;，Solmyr 键入了下面的代码： <br><br>void send_a_greeting(connection c) <br>{ <br>&nbsp;&nbsp;&nbsp;c.write("Hello!"); <br>} <br><br>zero 没费什么劲就看出了问题：&#8220;函数的设计者以为他是在向调用者传入的链接发送消息，但实际上这个函数在按值传递参数的时候创建了一个新链接。&#8221; <br><br>Solmyr
点了点头，继续说到：&#8220;还有，从扩展性的角度考虑，也应该禁止拷贝。比如，假设你将来打算控制链接的创建，把创建过程封装起来，那么这个拷贝构造函数就在
你的封装上捅了一个大窟窿 ——— 每个人都可以很方便的利用拷贝构造任意创建链接；又比如，假设将来你需要支持多个类型的链接，要把
connection 作为一个类层次的接口基类，那时，connection
的拷贝构造就必须要禁止，而你之前支持拷贝构造带来的代价就是辛苦的翻遍之前所有的代码去掉所有拷贝构造。&#8221; <br><br>&#8220;那，如果我确实需要在多处访问一个链接，该 &#8230;&#8230;&#8221; zero 没等 Solmyr 回答，自己就接了上去，&#8220;呃，也很简单，只要传递引用就可以了，或者如果需要更好的控制，可以用智能指针什么的。&#8221; <br><br>&#8220;完
全正确。说起来，其实许多类 ——— 比许多人所认为的要多的多 ———
所表示的概念对于&#8216;拷贝&#8217;这个动作都没有清楚的定义，比如常见的&#8216;窗口&#8217;、&#8216;文件&#8217;、&#8216;事务&#8217;等等等等，禁止它们拷贝往往可以让代码的逻辑清楚许多。以后
你在设计类的时候，完全可以首先考虑是否禁止它的拷贝构造，如果不能禁止，再去考虑怎么写拷贝构造函数的问题。好了 zero ，现在你能给出
connection 的实现吗？&#8221; <br><br>&#8220;Sure！只要将拷贝构造函数和重载赋值运算符设为私有，就可以禁止拷贝了。&#8221;zero 拖过键盘，三两下屏幕上就出现了一个新的实现： <br><br>class connection <br>{ <br>private: <br>&nbsp;&nbsp;&nbsp;conn_attr m_attr; <br>&nbsp;&nbsp;&nbsp;int m_bufsize; <br>&nbsp;&nbsp;&nbsp;char* m_buf; <br><br>&nbsp;&nbsp;&nbsp;conn_handle m_h; <br>&nbsp;&nbsp;&nbsp;... <br><br>public: <br>&nbsp;&nbsp;&nbsp;connection(const conn_attr&amp; attr, int bufsize) <br>&nbsp;&nbsp;&nbsp;: m_attr(attr), m_bufsize(bufsize) <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_buf = new char[m_bufsize]; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_h = open_conn(&amp;m_attr, m_buf, m_bufsize); <br>&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;~connection() <br>&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close_conn(m_h); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete m_buf; <br>&nbsp;&nbsp;&nbsp;} <br><br>&nbsp;&nbsp;&nbsp;void write(char c){ write_conn(m_h, c); } <br>&nbsp;&nbsp;&nbsp;char read(){ return read_conn(m_h); } <br>&nbsp;&nbsp;&nbsp;... <br><br>private: <br>&nbsp;&nbsp;&nbsp;connection(const connection&amp;); <br>&nbsp;&nbsp;&nbsp;connection&amp; operator=(const connection&amp;); <br>}; <br><br>&#8220;嗯，很好，这个问题可以告一段落了。&#8221;Solmyr 点了点头，准备离开，但又停了下来：&#8220;对了 zero ，pisces 他们这边曾经打报告要求增加人手，从今天的情况来看也确实需要有个懂点 C++ 的人加强这边。我看你正好有空，这个事就你来负责吧。&#8221; <br><br>zero 心中暗暗叫苦，赶紧分辨：&#8220;没有啊 Solmyr，我现在手边的事情多得做不完啊！&#8221; <br><br>&#8220;是吗？哦 &#8230;&#8230; 对了，我刚才接到网管的报告，说有个人的电脑最近频繁的访问 QQ 的服务器，那个人是谁来着？&#8221;，Solmyr 又露出了他招牌式的微笑。 <br><br>&#8220;呃 &#8230;&#8230; 我又想了想，虽然我确实事情比较多，但团队合作精神还是要发扬的。&#8221; <br><br>&#8220;嗯，这样就好。&#8221; Solmyr 心满意足的离开了。 <br><br>&#8220;真见鬼！&#8221;确认 Solmyr 走远后，zero 才把在心里憋着的抱怨吐了出来：&#8220;好不容易有一段可以休息休息的空档，这下子又泡汤了！真该死。&#8221;正在 zero 忿忿不平的时候，一个幽幽的声音从旁边飘了过来： <br><br>&#8220;zero，刚才你和 Solmyr 讲的什么&#8216;概念&#8217;、&#8216;禁止拷贝&#8217;、&#8216;类层次&#8217;&#8230;&#8230; 这些都是什么呀？还是你给我讲讲吧 ～～&#8221; <br><br>zero 转过头，看到 pisces 又在以非常&#8220;诚恳&#8221;的眼神看着他，再想到自己今后的任务，突然间觉得脑袋隐隐的痛了起来 ——— 他似乎有一点明白了，为什么没事的时候 Solmyr 总在揉自己的太阳穴 &#8230;&#8230; <img src ="http://www.cppblog.com/xmli/aggbug/93800.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 11:38 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93800.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之七：异常</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93798.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 03:28:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93798.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93798.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93798.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93798.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93798.html</trackback:ping><description><![CDATA[<meta http-equiv="CONTENT-TYPE" content="text/html; charset="utf-8"">
<title></title>
<meta name="GENERATOR" content="OpenOffice.org 3.1  (Win32)"><style type="text/css">
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
A:link { so-language: zxx }
-->
</style>
<p style="margin-bottom: 0cm;" align="LEFT"><span lang="zh-CN"><span style="font-style: normal;">大雨。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">乌云象铅块一样低低的压了下来，豆大的雨滴打的玻璃窗啪啪作响，难得一见的异常天气正在竭力表现它令人讨厌的一面。不过这一切似
乎并没有影响到
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr</span></font><span lang="zh-CN"><span style="font-style: normal;">，他仍然以他习惯的舒适姿势半躺在宽大的椅子里，手里还托着一杯热腾腾的果汁，在他背后，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">在键盘上敲打着什么。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">唉，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">，标准库中的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">怎么会是这个样子？设计糟透了。&#8221;</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">停止了工作，转过身来面对
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">，看起来有些困惑。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">胡乱批评被纳入神圣标准的成员是会遭天遣的。&#8221;</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">低着头，以一种算命先生似的语调答道。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">不知道上天是否打算加强
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">的说服力，恰在此时天空划过一道闪电，蓝白色的电光挣扎着努力向地面扑来，紧接着就是&#8220;喀喇&#8221;一声巨响
——— 这个雷很近。 </span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">一秒钟前还在想&#8220;这未免也太扯了&#8221;的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">表情一下子变得很古怪，良久才恢复正常。他标出了两行代码接着说到：&#8220;好、好吧，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr</span></font><span lang="zh-CN"><span style="font-style: normal;">，那请你解释一下为什么
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">的界面是这个样子。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>std::stack&lt;int&gt;
si; <br>&#8230;&#8230; <br>int i = si.top(); <br>si.pop(); <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">只要让
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">pop()
</span></font><span lang="zh-CN"><span style="font-style: normal;">返回栈顶元素就可以把上面两行合成一行，而且更加直观，为什么要搞成现在这样？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">目睹了
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">表情变化的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">强忍住放声大笑的冲动
——— 老天知道他忍的有多辛苦 ———
缓缓的把杯子放到桌上，转过身来开始讲解这个问题：
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">原因在于异常。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">异常？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">对，
很多代码在没有异常的时候工作的挺好，但是一旦出现异常就变得不可收拾，就像一间茅草屋，平时看起来没什么问题，一遇到今天这种天气
&#8230;&#8230; &#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">指了指窗外，&#8220;
&#8230;&#8230; 立刻就会垮掉。考虑一下如果 </span></span><font face="Times New Roman, serif"><span style="font-style: normal;">pop()
</span></font><span lang="zh-CN"><span style="font-style: normal;">返回栈顶元素需要怎样实现，假设栈内部用数组实现，且不考虑栈是否为空的问题。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">很简单啊。&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">打开了编辑器，写下：
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>template
&lt;typename T&gt; <br>T stack&lt;T&gt;::pop() <br>{ <br>&nbsp;&nbsp;&nbsp;...
... <br>&nbsp;&nbsp;&nbsp;return data[top--]; // </span></font><span lang="zh-CN"><span style="font-style: normal;">假设数据存储于数组
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">data
</span></font><span lang="zh-CN"><span style="font-style: normal;">中，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">top
</span></font><span lang="zh-CN"><span style="font-style: normal;">代表栈顶位置
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>}
<br><br>Solmyr </span></font><span lang="zh-CN"><span style="font-style: normal;">摇摇头：&#8220;这就是茅草屋。要知道
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">是个模板类，它存放的元素
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">T
</span></font><span lang="zh-CN"><span style="font-style: normal;">可能是用户定义的类。我来问你，如果类型
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">T
</span></font><span lang="zh-CN"><span style="font-style: normal;">的拷贝构造函数抛出异常，会出现什么情况？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">嗯
&#8230;&#8230; 按值返回，返回值是个临时对象，该临时对象以
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">data[top]
</span></font><span lang="zh-CN"><span style="font-style: normal;">拷贝构造
&#8230;&#8230; 嗯，这样一来函数返回时可能抛出异常，客户此时无法取得该元素。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">还有呢？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">还有？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">提示，你的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">top
</span></font><span lang="zh-CN"><span style="font-style: normal;">怎么了？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;
&#8230;&#8230; </span></font><span lang="zh-CN"><span style="font-style: normal;">哎呀！糟了！</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">top
</span></font><span lang="zh-CN"><span style="font-style: normal;">此时已经减一，栈顶元素就此丢失了！这样的话
&#8230;&#8230; 必须实现一个函数允许客户修改 &#8230;&#8230;&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">说不下去了。他想了一会，摇摇头承认失败：&#8220;不行，这里拷贝构造发生在函数返回之后，无论如何无法避免这种情况。只能在文档里写明：要求
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">T
</span></font><span lang="zh-CN"><span style="font-style: normal;">的拷贝构造函数不抛出异常。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">停了一停，小心翼翼的问
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">：&#8220;这个不算过分的要求吧？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">的回答异常简短：&#8220;</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">new&#8221;
<br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">哦对，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">new
</span></font><span lang="zh-CN"><span style="font-style: normal;">在分配内存失败时会抛出
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">std::bad_alloc
&#8230;&#8230; </span></font><span lang="zh-CN"><span style="font-style: normal;">算我没说。</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">，我明白了，为了处理异常的情况，调整栈顶位置必须在所有数据拷贝完成之后，所以按值返回是不可接受的。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">正确。所以对于一个设计目标是最大限度可复用性的标准库成员而言，这是不可接受的。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">顿了顿，继续说到：&#8220;而且异常带来的影响远不止此。我刚才说&#8216;假设栈内部用数组实现&#8217;，但如果你充分考虑抛出异常的各种可能性，你就会发现用数组实现是糟糕的主意。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;
&#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230;
</span></font><span lang="zh-CN"><span style="font-style: normal;">这是为什么？在没有传值返回的情况下，我们总可以捕捉到发生的异常并加以处理啊？&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">谨慎的发问。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">赞许的看着
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">。&#8220;发问之前先自行思考，习惯不错。&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">心想，但是脸上一点也没表现出来：&#8220;没错，但捕捉到异常不代表你总能正确的处理它。考虑一下
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">的赋值运算符，如果我们用数组来实现，那么在拷贝数据的时候肯定会有类似这样的一个循环：&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>//
</span></font><span lang="zh-CN"><span style="font-style: normal;">各变量的意义与上面相同
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>template
&lt;typename T&gt; <br>stack&lt;T&gt;&amp; stack&lt;T&gt;::perator=(const
stack&lt;T&gt;&amp; rhs) <br>{ <br>&nbsp;&nbsp;&nbsp;... ...
<br>&nbsp;&nbsp;&nbsp;for(int i=0; i&lt;rhs.top; i++) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data
</span><span style="font-style: normal;">= rhs.data; <br>&nbsp;&nbsp;&nbsp;...
... <br>} <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">现在考虑类型
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">T
</span></font><span lang="zh-CN"><span style="font-style: normal;">的赋值运算符可能抛出异常，该怎样修改上面的代码。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">停了下来，再度捧起了杯子。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">用
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">try
</span></font><span lang="zh-CN"><span style="font-style: normal;">把
&#8230;&#8230; 哦 &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230; &#8230;&#8230;&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">似乎发现了问题所在，沉默良久，才接着说到：&#8220;这个循环可能在运行到一半的时候抛出异常，这样会导致一部分数据已经成功赋值，另一部分却还是老的。除非我
们用 </span></span><font face="Times New Roman, serif"><span style="font-style: normal;">catch(...)
</span></font><span lang="zh-CN"><span style="font-style: normal;">捕捉所有异常，忽略之并继续赋值。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">但是这样
&#8230;&#8230;&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">有意识的引导
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">继续深入思考。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;&#8230;&#8230;
</span></font><span lang="zh-CN"><span style="font-style: normal;">但是这样，赋值运算符抛出的异常就被我们&#8216;吃掉了&#8217;，异常总是代表着某些不该发生的事情发生了，所以应该让客户接收到这个异常才对。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">皱着眉头，一字一顿，显得相当辛苦。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">正
确。</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">作为一个通用的标准库成员，在面对异常时必须做到两点。一、异常安全，也就是说异常不会导致它本身处于一种错误的状态或是导致数据丢失或是造成资源泄漏；
二、异常透明，也就是说客户代码 ——— 这里指它存放的类型
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">T
</span></font><span lang="zh-CN"><span style="font-style: normal;">的实现
——— 抛出的任何异常，不应该被&#8216;吃掉&#8217;或者被改变，应该透明的传递给客户。一望即知，上面的代码无可能同时做到这两点。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">是这样，我懂了，这大概就是标准库中的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">不用数组实现的主要原因了吧&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">露出了很有把握的神情。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">当然不是！有点常识好不好，用数组实现的话
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">的大小固定，这怎么能够接受呢？！&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">又一次的，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">目睹了
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">表情发生难以言喻的剧烈变化。这次他没能忍住放声大笑的冲动，连杯子里的果汁也洒了出来，一时间，笑声充满了整个办公室
——— 不仅仅是他的，还包括了（众位看官应该猜的到吧？）围观同事们的笑声。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">驱散了围观者之后，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">面带愠色的坐下：&#8220;有那么好笑吗？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">抱
歉抱歉，我 &#8230;&#8230; 哈哈哈 &#8230;&#8230; 我 &#8230;&#8230; 哈哈 &#8230;&#8230;
我只是一时忍不住 &#8230;&#8230; 哈哈哈哈 &#8230;&#8230; &#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">好容易平息了大笑，坐直了身子，放下了果汁，正色道：&#8220;关键在于上面引入的应该遵循的两条原则，也就是异常安全，和异常透明。现在你考虑一下如果
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">内部的数据以指针存放，怎样在赋值运算符中保证上述两点？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;
&#8230;&#8230; </span></font><span lang="zh-CN"><span style="font-style: normal;">嗯
&#8230;&#8230; 还是会有上面那样一个循环 &#8230;&#8230; 呃 &#8230;&#8230; &#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">面有难色。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">提示，不一定非得直接拷贝到
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">stack
</span></font><span lang="zh-CN"><span style="font-style: normal;">保存数据的内存里。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;
&#8230;&#8230; </span></font><span lang="zh-CN"><span style="font-style: normal;">嗯
&#8230;&#8230; 不直接拷贝，那么就是 &#8230;&#8230; 就是拷贝到 &#8230;&#8230;
啊！我明白了！&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">抓住了其中的关键，飞快的写下：
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>//
pdata </span></font><span lang="zh-CN"><span style="font-style: normal;">代表指向存放数据内存的指针，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">top
</span></font><span lang="zh-CN"><span style="font-style: normal;">代表栈顶元素的偏移量
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>template
&lt;typename T&gt; <br>stack&lt;T&gt;&amp; stack&lt;T&gt;::perator=(const
stack&lt;T&gt;&amp; rhs) <br>{ <br>&nbsp;&nbsp;&nbsp;... ... <br>&nbsp;&nbsp;&nbsp;T*
ptemp = new T[rhs.top]; <br>&nbsp;&nbsp;&nbsp;try <br>&nbsp;&nbsp;&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int i=0; i&lt;rhs.top;
i++) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(ptemp+i)
= *(rhs.pdata+i); <br>&nbsp;&nbsp;&nbsp;} <br>&nbsp;&nbsp;&nbsp;catch(...)
&nbsp;// </span></font><span lang="zh-CN"><span style="font-style: normal;">捕捉可能出现的异常
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>&nbsp;&nbsp;&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete[] ptemp;
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw; &nbsp;// </span></font><span lang="zh-CN"><span style="font-style: normal;">重新抛出
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>&nbsp;&nbsp;&nbsp;}
<br><br>&nbsp;&nbsp;&nbsp;delete[] pdata; &nbsp;// </span></font><span lang="zh-CN"><span style="font-style: normal;">释放当前的内存
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>&nbsp;&nbsp;&nbsp;pdata
= ptemp; &nbsp;// </span></font><span lang="zh-CN"><span style="font-style: normal;">让
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">pdata
</span></font><span lang="zh-CN"><span style="font-style: normal;">指向赋值成功的内存块
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>&nbsp;&nbsp;&nbsp;...
... <br>} <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">只
要这样&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">边输入边说，&#8220;只要先把数据拷贝到一个临时分配的缓冲区，在此过程中处理异常，然后让
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">pdata
</span></font><span lang="zh-CN"><span style="font-style: normal;">指向成功分配的内存就行了。这里的关键是让拷贝动作成为可以
&#8230;&#8230; 呃 &#8230;&#8230; 可以安全的取消的，剩下的赋值动作就是简单的指针赋值，肯定不会抛出异常了。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">非常好。值得指出的是，这是一种相当常见的手段，有个名字叫做
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">copy
&amp; swap </span></font><span lang="zh-CN"><span style="font-style: normal;">，它不仅仅可以用来应付异常，也可以有效的实现一些其他特征。</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">OK</span></font><span lang="zh-CN"><span style="font-style: normal;">，这个问题大概就是这样了。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">问题似乎可以告一段落了，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">开始打算就此结束这个话题。可
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">疑惑的表情阻止了他。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">还有什么问题吗？</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">啊
&#8230;&#8230;
没什么，我只是在想，异常导致了这么多麻烦，这一次，还有上一次的线程死锁问题（参见&#8220;小品文系列&#8221;的前一篇，&#8220;成对出现&#8221;）都是因为异常的存在才会变得如此复杂的，那为什么
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">C++
</span></font><span lang="zh-CN"><span style="font-style: normal;">还要支持它呢？有错误完全可以在返回值里报告嘛。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">嗯，
这确实是个常见的疑惑，不过答案也很简单，异常的存在有它自己的价值。一、使用异常报告错误可以避免污染函数界面；二、如果你希望报告比较丰富的错误信
息，使用一个异常对象比简单的返回值要有效的多，而且避免了返回复杂对象造成的开销；三、也是我认为比较重要的，有些错误不合适用返回值来报告。举个例
子，动态内存分配。我问你，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">C
</span></font><span lang="zh-CN"><span style="font-style: normal;">语言中怎样报告动态内存分配错误？&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">转过头来看着
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;malloc
</span></font><span lang="zh-CN"><span style="font-style: normal;">函数返回一个
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">NULL
</span></font><span lang="zh-CN"><span style="font-style: normal;">值代表动态内存分配错误。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">但是你见过多少
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">C
</span></font><span lang="zh-CN"><span style="font-style: normal;">程序员在每次使用
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">malloc
</span></font><span lang="zh-CN"><span style="font-style: normal;">之后都检查返回值？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;
&#8230;&#8230; &#8221; <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">没有是吗？这很正常，每次使用
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">malloc
</span></font><span lang="zh-CN"><span style="font-style: normal;">之后检查返回值是件令人痛苦的事情，所以即使有
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Steve
Maguire</span></font><span lang="zh-CN"><span style="font-style: normal;">（注：《</span></span><font face="Times New Roman, serif"><a  href="http://www.fifid.com/search/Writing+Clean+Code?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366"><span style="font-style: normal;">Writing
Clean Code</span></font></a></font><span lang="zh-CN"><span style="font-style: normal;">》一书的作者）这样的老程序员谆谆教导、耳提面命，还是有数以万计的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">C
</span></font><span lang="zh-CN"><span style="font-style: normal;">程序中存在这样的代码：&#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">顺手键入：
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>/*
</span></font><span lang="zh-CN"><span style="font-style: normal;">传统
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">C
</span></font><span lang="zh-CN"><span style="font-style: normal;">程序
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">*/
<br>int* p = malloc( sizeof(int) ); <br>*p = 10; <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">一旦
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">malloc
</span></font><span lang="zh-CN"><span style="font-style: normal;">失败返回
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">NULL</span></font><span lang="zh-CN"><span style="font-style: normal;">，这个程序就会崩溃。然而如果是
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">C++
</span></font><span lang="zh-CN"><span style="font-style: normal;">程序，使用
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">new
</span></font><span lang="zh-CN"><span style="font-style: normal;">的话
&#8230;&#8230; &#8221;，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">键入了对应的代码：
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>//
C++ </span></font><span lang="zh-CN"><span style="font-style: normal;">程序
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br>int*
p = new int; <br>*p = 10; <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">就不存在这样的问题。我问你，这是为什么？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">很快找到了答案：&#8220;因为如果
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">new
</span></font><span lang="zh-CN"><span style="font-style: normal;">失败，它会抛出
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">std::bad_alloc
</span></font><span lang="zh-CN"><span style="font-style: normal;">异常，于是函数在此中断、退出，下面这一行也就不会被调用了。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">正
确。而且你不必在每一处处理这个异常，你只要保证你的程序对异常透明，就可以在
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">main
</span></font><span lang="zh-CN"><span style="font-style: normal;">函数中写下
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">try
... catch </span></font><span lang="zh-CN"><span style="font-style: normal;">对，捕获所有未捕获的异常。比如你可以在
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">main
</span></font><span lang="zh-CN"><span style="font-style: normal;">函数中捕捉
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">std::bad_alloc</span></font><span lang="zh-CN"><span style="font-style: normal;">，在输出&#8216;内存不足&#8217;错误信息，然后保存所有未保存的数据，完成所有的清理工作，最后结束程序。一言以蔽之，体面的退出。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">点着头，喃喃的重复着：&#8220;对，体面的退出。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">见
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">领会了他的意思，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">继续开始下一个议题：&#8220;异常的存在还有最后一个重要价值
——— 也是当初设计它的初衷之一 ———
提供一个通用的手段让构造函数可以方便的报告错误：因为构造函数没有返回值。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">还有析构函数也是。&#8221;没等
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">说完，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">就加上了这一句。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">对着自作聪明的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">摇了摇头：&#8220;不要想当然，关于异常有一个非常重要的原则：永远不要让你的析构函数抛出异常。知道为什么吗？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;
&#8230;&#8230; </span></font><span lang="zh-CN"><span style="font-style: normal;">不知道。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">这次决定老实承认。
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">因为抛出异常的析构函数会导致最简单的程序无法正确运行，比如下面两句：&#8221;这次出现在屏幕上的，是看来似乎毫无瑕疵的两行代码：
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>evil
p = new evil[10]; <br>delete[] p; <br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">看上去一点问题也没有是么？仔细分析一下
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">delete[]
p </span></font><span lang="zh-CN"><span style="font-style: normal;">这一句，它会调用
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">10
</span></font><span lang="zh-CN"><span style="font-style: normal;">次
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">evil
</span></font><span lang="zh-CN"><span style="font-style: normal;">类的析构函数，假设其中第
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">5
</span></font><span lang="zh-CN"><span style="font-style: normal;">次
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">evil
</span></font><span lang="zh-CN"><span style="font-style: normal;">类的析构函数抛出异常，会出现什么情况？&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">陷入了沉思，视线盯着屏幕一动不动，神情看起来就象是一段执行复杂运算的程序，而且是没有输出的那种。不过没多久，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">就换了一种表情，这种表情通常被形容为胸有成竹：&#8220;我知道了
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">，在这种情况下，</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">delete[]
</span></font><span lang="zh-CN"><span style="font-style: normal;">面临两难选择。选择一是不捕捉这个异常，让它传播到调用者那里，但这样一来
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">delete[]
</span></font><span lang="zh-CN"><span style="font-style: normal;">就被中断了，后面的
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">5
</span></font><span lang="zh-CN"><span style="font-style: normal;">个
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">evil
</span></font><span lang="zh-CN"><span style="font-style: normal;">对象占用的内存就会无法释放，导致资源泄漏；选择二是捕捉这个异常以防止资源泄漏，但这样一来这个异常就被
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">delete[]
</span></font><span lang="zh-CN"><span style="font-style: normal;">吃掉了，违反了&#8216;对异常透明&#8217;的原则。所以无论怎么做，都没法妥善的处理析构函数抛出异常的情况。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">赞许的点头：&#8220;非常好。接下来，你的任务是
&#8230;&#8230;&#8221; </span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>&#8220;</span></font><span lang="zh-CN"><span style="font-style: normal;">我知道我知道，把这些讨论整理成文档是吧？我这就动手。&#8221;
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br>zero
</span></font><span lang="zh-CN"><span style="font-style: normal;">转过身去，开始埋头于他的文档。而
</span></span><font face="Times New Roman, serif"><span style="font-style: normal;">Solmyr
</span></font><span lang="zh-CN"><span style="font-style: normal;">则再度恢复了半躺半坐的舒适姿势，捧起了他的果汁，并且略略有些意外的发现
——— </span></span><font face="Times New Roman, serif"><span style="font-style: normal;"><br><br></span></font><span lang="zh-CN"><span style="font-style: normal;">天气放晴了。
</span></span>
</p><img src ="http://www.cppblog.com/xmli/aggbug/93798.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 11:28 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93798.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之六：成对出现</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93797.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 03:17:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93797.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93797.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93797.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93797.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93797.html</trackback:ping><description><![CDATA[&#8220;呼 ～～～～ 啪！&#8221; <br><br>一个文件夹划出一道优美的弧线，越过四张桌子，两堵隔墙，一条走道，不偏不倚的穿过了正在交谈的路人甲和路人乙，精准的命中了目标。放眼公司上下，拥有这般投掷手法的，只有 Solmyr ，而他的目标，自然是 zero 了。 <br><br>&#8220;哎哟！&#8221;，zero 摸了摸被击中的后脑勺，一半不甘一半认命的叹了一口气：不用问，他一定又有什么把柄被 Solmyr 抓住了。 <br><br>&#8220;这次我又犯了什么错误了？&#8221;，zero 匆匆中断了与方圆五十米内唯一的女程序员 pisces 之间愉快的闲聊，来到 Solmyr 身边看看究竟哪里出了不妥。 <br><br>&#8220;你刚刚提交的代码会导致线程死锁&#8221;，Solmyr 指着 zero 提交的一个函数： <br><br>void some_func() <br>{ <br>pthread_mutex_lock(&amp;mtx); <br>&#8230;&#8230; <br>&#8230;&#8230; <br>pthread_mutex_unlock(&amp;mtx); <br>} <br><br>&#8220;会吗？我明明在函数末尾释放了互斥变量的呀？&#8221; <br><br>Solmyr 看了看 zero ，那表情分明在说：朽木不可雕也。他顺手标出了函数中间的两行代码： <br><br>void some_func() <br>{ <br>pthread_mutex_lock(&amp;mtx); <br>&#8230;&#8230; <br>if( status == E_FAIL ) <br>&nbsp;return; <br>&#8230;&#8230; <br>pthread_mutex_unlock(&amp;mtx); <br>} <br><br>&#8220;Oops！&#8221;，zero 拍了一下脑门，&#8220;我知道了我知道了，我这就改。&#8221; <br><br>&#8220;你知道了？说说看你犯了什么错误？&#8221; <br><br>&#8220;我忘了在中间的函数返回点解锁。&#8221; <br><br>&#8220;那你准备怎么解决这个问题&#8221;，很明显，Solmyr 不打算就此轻轻放过 zero。 <br><br>&#8220;嗯 &#8230;&#8230; 很简单啊，在这里加上一行代码，象这样：&#8221; <br><br>if( status == E_FAIL ) <br>{ <br>pthread_mutex_unlock(mtx); <br>return; <br>} <br><br>Solmyr 摇摇头：&#8220;你这是头痛医头，脚痛医脚。如果你这个函数里不只一个锁，不只一个返回点，你打算怎么做？在每个返回点解开每个锁么？&#8221; <br><br>&#8220;嗯 &#8230;&#8230; 你是指我应该遵循一个函数只有一个返回点的原则？&#8221;，zero 挠挠头，有些不太确定。 <br><br>&#8220;我不是指这个。有些情况下，硬要让函数只有一个返回点会导致巨大的 if/else 结构，降低代码的可读性。而且，即使你的函数只有一个返回点，你还是有可能遇到这个问题。考虑这样的函数：&#8221;，Solmyr 飞快的键入： <br><br>void some_func() <br>{ <br>pthread_mutex_lock(&amp;mtx); <br>&#8230;&#8230; <br>// 中间没有其他返回点 <br>&#8230;&#8230; <br>foo(); // 由其他程序员实现的函数 <br>&#8230;&#8230; <br>pthread_mutex_unlock(&amp;mtx); <br>} <br><br>&#8220;看起来一点问题也没有，可是如果 foo 这个函数丢出异常的话，会出现什么情况？&#8221; <br><br>&#8220;嗯
&#8230;&#8230; 如果我们函数里没有捕获这个异常的话 &#8230;&#8230; 它会导致 some_func 函数在调用 foo 的这一点中断 &#8230;&#8230; 哎呀 &#8230;&#8230;&#8221;，zero
发现了问题所在。&#8220;那么只能在每个可能抛出异常的函数调用点用 try 捕获所有异常，然后 &#8230;&#8230;&#8221;，zero 越说越小声，&#8220; &#8230;&#8230; 然后在
catch 里面解锁，再重新抛出 &#8230;&#8230;&#8221; zero 停了下来，烦恼的挠着头，发现他连自己都说服不了：这样的解法实在是太繁琐、太容易引入错误了。
<br><br>&#8220;嗯？&#8221; <br><br>&#8220;好吧，我承认我不知道该怎么办了，Solmyr ，这种情况应该怎么处理呢？&#8221; <br><br>&#8220;回忆一下，前两天我们在饭桌上讨论过什么？&#8221;（参见&#8220;Solmyr 的小品文系列&#8221;的前一期，&#8220;垃圾收集&#8221;） <br><br>&#8220;你是说垃圾收集吗？哎 &#8230;&#8230; 可是 &#8230;&#8230; 那是处理内存泄漏的呀？和这个问题有什么关系？&#8221; <br><br>&#8220;我不是指具体的解法，&#8221;，Solmyr 摇摇头，&#8220;关键是上次讨论中引入的具有普遍性的原则，也就是 &#8230;&#8230;&#8221; Solmyr 停了下来，转头看着 zero 。 <br><br>&#8220;&#8230;&#8230; &#8230;&#8230;&#8221; <br><br>&#8220;唉 &#8230;&#8230;&#8221;，Solmyr 用别人模仿不来的无奈表情 —— 按照他自己的说法，这是多年培训工作的积累 —— 叹了口气：&#8220;我说 zero，你还很年轻，不会这么早就记忆力衰退了吧？&#8221; <br><br>&#8230;&#8230; 真是可恶的家伙，zero 心中恨恨的想。 <br><br>Solmyr 的声音再度在 zero 接近崩溃边缘的时候响了起来：&#8220;如果你希望保证某些事情成对出现，请使用 &#8230;&#8230;&#8221; <br><br>&#8220;构造函数与析构函数！&#8221;，zero 生怕错过了显示自己并非&#8220;记忆力衰退&#8221;的机会。 <br><br>&#8220;不用喊那么大声。&#8221;，Solmyr 皱了皱眉，&#8220;你把前排观众都吓坏了。&#8221; <br><br>&#8220;？！！！&#8221;，zero 迅速转身，发现附近不知什么时候围满了公司的同事，每个人都&#8220;正常&#8221;在做自己的事情，只是动作稍显忙乱而已 &#8230;&#8230; <br><br>解决了四周的&#8220;观众&#8221;之后，zero 回到了显示器前，信心满满：&#8220;我知道了 Solmyr ，这里我们可以用和上次处理 分配/释放 内存非常类似的手段来处理 加锁/解锁，只要写一个非常简单的类就行了，象这样：&#8221;，zero 一边说，一边键入： <br><br>class auto_lock <br>{ <br>public: <br>auto_lock(pthread_mutex_t mtx) : m_mtx(mtx) <br>{ <br>&nbsp;pthread_mutex_lock(&amp;m_mtx); // 构造时加锁 <br>} <br>~auto_lock() <br>{ <br>&nbsp;pthread_mutex_unlock(&amp;m_mtx); // 析构时解锁 <br>} <br><br>private: <br>pthread_mutex_t&amp; m_mtx; <br>} <br><br>void some_func() <br>{ <br>auto_lock(mtx); <br>&#8230;&#8230; <br>// return 、foo ，随便什么东西都行 <br>&#8230;&#8230; <br>// 结束的时候同样不用解锁 <br>} <br><br>&#8220;这样一来，我之前遇到的问题就全解决了，我可以自由的实现我的函数，不论什么时候返回或者遇到异常，我都可以肯定 mtx 将会被解锁，不用担心线程死锁的问题。&#8221; <br><br>&#8220;嗯，
不错。&#8221; Solmyr
赞许的点了点头，开始总结：&#8220;实际上这是一个非常常用的手段，除了我们讨论过的两种情况而外，还可以应用在很多场合。比如网络访问中的建立连接和断开连
接，数据库访问中的登录与退出登录，还可以方便的用它来实现测量一个函数平均运行耗时的测试工具，等等等等。不过万变不离其宗，在这一切应用的背后是一个
统一的原则 &#8230;&#8230;&#8221; <br><br>Solmyr 顿了一顿，zero 心领神会的接了上去： <br><br>&#8220;如果你希望保证某些事情成对出现，请使用构造函数与析构函数。&#8221; <img src ="http://www.cppblog.com/xmli/aggbug/93797.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 11:17 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93797.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之五：垃圾收集</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93796.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 03:05:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93796.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93796.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93796.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93796.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93796.html</trackback:ping><description><![CDATA[午餐时间。 <br>zero 坐在餐桌前，机械的重复&#8220;夹菜 -&gt; 咀嚼 -&gt;
吞咽&#8221;的动作序列，脸上用无形的大字写着：我心不在焉。在他的对面坐着 Solmyr ，慢条斯理的吃着他那份午餐，维持着他一贯很有修养的形象
——— 或者按照 zero 这些熟悉他本质的人的说法：假象。 <br><br>&#8220;怎么了 zero ？胃口不好么？&#8221;，基本填饱肚子之后，Solmyr 觉得似乎应该关心一下他的学徒了。 <br><br>&#8220;呃，没什么，只是 &#8230;&#8230; Solmyr ，C++ 为什么不支持垃圾收集呢？（注：垃圾收集是一种机制，保证动态分配了的内存块会自动释放，Java 等语言支持这一机制。）&#8221; <br><br>Solmyr 叹了口气，用一种平静的眼神盯着 zero ：&#8220;是不是在 BBS 上和人吵 C++ 和 Java 哪个更好？而且吵输了？我早告诉过你，这种争论再无聊不过了。&#8221; <br><br>&#8220;呃 &#8230;&#8230; 是&#8221;，zero 不得不承认 ——— Solmyr 的眼神虽然一点也不锐利，但是却莫名其妙的让 zero 产生了微微的恐惧感。 <br><br>&#8220;而且，谁告诉你 C++ 不支持垃圾收集的？&#8221; <br><br>&#8220;啊！Solmyr 你不是开玩笑吧？！&#8221; <br><br>&#8220;zero 你得转变一下观念。我问你，C++ 支不支持可以动态改变大小的数组？&#8221; <br><br>&#8220;这 &#8230;&#8230; 好象也没有吧？&#8221; <br><br>&#8220;那 vector 是什么东西？&#8221; <br><br>&#8220;呃 &#8230;&#8230;&#8221; <br><br>&#8220;支持一种特性，并不是说非得把这个特性加到语法里去，我们也可以选择用现有的语言机制实现一个库来支持这个特征。以垃圾收集为例，这里我们的任务是要保证每一个被动态分配的内存块都能够被释放，也就是说 &#8230;&#8230;&#8221;，Solmyr 不知从哪里找出了一张纸、一支笔，写到： <br><br>int* p = new int;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 1 <br>delete p;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 2 <br><br>&#8220;也就是说，对于每一个 1 ，我们要保证有一个 2 被调用，1 和 2 必须成对出现。我来问你，C++ 中有什么东西是由语言本身保证一定成对出现的？&#8221; <br><br>&#8220;&#8230;&#8230;&#8221;，zero 露出了努力搜索记忆的表情，不过很明显一无所获。 <br><br>&#8220;提示一下，和类的创建有关。&#8221; <br><br>&#8220;哦！构造函数与析构函数！&#8221; <br><br>&#8220;正确。可惜普通指针没有构造函数与析构函数，所以我们必须要写一个类来加一层包装，最简单的就象这样：&#8221; <br><br>class my_intptr <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; int* m_p; <br><br>&nbsp;&nbsp;&nbsp; my_intptr(int* p){ m_p = p; } <br>&nbsp;&nbsp;&nbsp; ~my_intptr(){ delete m_p; } <br>}; <br><br>&#8230;&#8230;&#8230;&#8230; <br><br>my_intptr pi(new int); <br>*(pi.m_p) = 10; <br><br>&#8230;&#8230;&#8230;&#8230; <br><br>&#8220;这
里我们可以放心的使用 my_intptr ，不用担心内存泄漏的问题：一旦 pi 这个变量被销毁，我们知道 pi.p
指向的内存块一定会被释放。不过如果每次使用 my_intptr 都得去访问它的成员未免太麻烦了。为此，可以给这个类加上重载的 * 运算符：&#8221; <br><br>class my_intptr <br>{ <br>private: <br>&nbsp;&nbsp;&nbsp; int* m_p; <br><br>public: <br>&nbsp;&nbsp;&nbsp; my_intptr(int* p){ m_p = p; } <br>&nbsp;&nbsp;&nbsp; ~my_intptr(){ delete m_p; } <br><br>&nbsp;&nbsp;&nbsp; int&amp; operator*(){ return *m_p; } <br>}; <br><br>&#8230;&#8230;&#8230;&#8230; <br><br>my_intptr pi; <br>*pi = 10; <br>int a = *pi; <br><br>&#8230;&#8230;&#8230;&#8230; <br><br>&#8220;现在是不是看起来 my_intptr 就像是一个真正的指针了？正因为如此，这种技术被称为智能指针。现在我问你，这个类还缺少哪些东西？&#8221; <br><br>zero 皱着眉头，眼睛一眨一眨，看上去就像一台慢速电脑正在辛苦的往它的硬盘上拷贝文件。良久，zero 抬起头来，不太确定的说：&#8220;是不是还缺少一个拷贝构造函数和一个赋值运算符？&#8221; <br><br>&#8220;说说为什么。&#8221;，Solmyr 显然不打算就这样放过 zero。 <br><br>&#8220;因为 &#8230;&#8230; 我记得没错的话 &#8230;&#8230; 《<a  href="http://www.fifid.com/search/50+%E8%AF%AB+?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366">50 诫 </font></a>》（注：
指《Effective C++
2/e》一书）中提到过，如果你的类里面有指针指向动态分配的内存，那么一定要为它写一个拷贝构造函数和一个赋值运算符 &#8230;&#8230; 因为 &#8230;&#8230;
否则的话，一旦你做了赋值，会导致两个对象的指针指向同一块内存。对了！如果是上面的类，这样一来会导致同一个指针被 delete 两次！&#8221; <br><br>&#8220;正确。那么我们应该怎样来实现呢？&#8221; <br><br>&#8220;这简单，我们用 memcpy 把目标指针指向的内存中的内容拷贝过来。&#8221; <br><br>&#8220;如果我们的智能指针指向一个类的对象怎么办？注意，类的对象中可能有指针，不能用 memcpy。&#8221; <br><br>&#8220;那 &#8230;&#8230; 我们用拷贝构造的办法。&#8221; <br><br>&#8220;如果我们的智能指针指向的对象不能拷贝构造怎么办？它可能有一个私有的拷贝构造函数。&#8221; <br><br>&#8220;那 &#8230;&#8230;&#8221;，zero 顿了一顿，决定老实承认，&#8220;我不知道。&#8221; <br><br>&#8220;问题在哪你知道么？在于你没有把智能指针看作指针。想象一下，如果我们对一个指针做赋值，它的含义是什么？&#8221; <br><br>&#8220;呃，我明白了，在这种情况下，应该想办法让两个智能指针指向同一个对象 &#8230;&#8230; 可是 Solmyr ，这样以来岂不是仍然要对同一个对象删除两遍？&#8221; <br><br>&#8220;是
的，我们得想办法解决这个问题，办法不只一种。比较好的一种是为每个指针维护一个引用计数值，每次赋值或者拷贝构造，就让计数值加一，这意味着指向这个内
存块的智能指针又多了一个；而每有一个智能指针被销毁，就让计数值减一，这意味着指向这个内存块的智能指针少了一个；一旦计数值为 0
，就释放内存块。象这样：&#8221; <br><br>class my_intptr <br>{ <br>private: <br>&nbsp;&nbsp;&nbsp; int* m_p; <br>&nbsp;&nbsp;&nbsp; int* m_count; <br><br>public: <br>&nbsp;&nbsp;&nbsp; my_intptr(int* p) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_p = p; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_count = new int;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; // 初始化计数值为 1 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *m_count = 1; <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; my_intptr(const my_intptr&amp; rhs)&nbsp;&nbsp;&nbsp; // 拷贝构造函数 <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m_p = rhs.m_p;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 指向同一块内存 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m_count = rhs.m_count;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; // 使用同一个计数值 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (*m_count)++;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 计数值加 1 <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; ~my_intptr() <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (*m_count)--;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; // 计数值减 1 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if( *m_count == 0 )&nbsp;&nbsp;&nbsp;&nbsp; // 已经没有别的指针指向该内存块了 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; delete m_p; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; delete m_count; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; } <br><br>&nbsp;&nbsp;&nbsp; my_intptr&amp; operator=(const my_intptr&amp; rhs) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if( m_p == rhs.m_p )&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 首先判断是否本来就指向同一内存块 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return *this;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 是则直接返回 <br><br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (*m_count)--;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 计数值减 1 ，因为该指针不再指向原来内存块了 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if( *m_count == 0 )&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; // 已经没有别的指针指向原来内存块了 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; delete m_p; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; delete m_count; <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } <br><br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m_p = rhs.m_p;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 指向同一块内存 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m_count = rhs.m_count;&nbsp;&nbsp;&nbsp; // 使用同一个计数值 <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (*m_count)++;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; // 计数值加 1 <br>&nbsp;&nbsp;&nbsp; } <br><br>&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230; <br>}; <br><br>&#8220;其他部分没有什么太大变化，我不费事了。现在想象一下我们怎样使用这种智能指针？&#8221;，Solmyr 放下了笔，再次拿起了筷子，有些惋惜的发现他爱吃的肉丸子已经冷了。 <br><br>zero
想象着，有些迟疑。&#8220;我们 &#8230;&#8230; 可以用 new int 表达式作为构造函数的参数来构造一个智能指针，然后 &#8230;&#8230;
然后我们可以任意的赋值，&#8221;，他开始抓住了思路，越说越快，&#8220;任意的用已经存在的智能指针来构造新的智能指针，智能指针的赋值运算符、拷贝构造函数和析构
会保证计数值始终等于指向该内存块的智能指针数。&#8221;zero 似乎明白了他看到了怎样的功能，开始激动起来：&#8220;然后一旦计数值为 0
被分配的内存块就会释放！也就是说 &#8230;&#8230;
有指针指向内存块，它就不释放，一旦没有，它就自动释放！太棒了！我们只要一开始正确的初始化智能指针，就可以象普通指针那样使用它，而且完全不用担心内
存释放的问题！太棒了！&#8221;zero 激动的大叫：&#8220;这就是垃圾收集！Solmyr ！我们在饭桌上实现了一个垃圾收集器！&#8221; <br><br>Solmyr 很明显没有分享 zero 的激动：&#8220;我在吃饭，你能不能不要大叫&#8216;饭桌上实现了一个垃圾收集器&#8217;这种倒胃口的话？&#8221;顿了一顿，Solmyr 带着他招牌式的坏笑，以一种可恶的口吻说道：&#8220;而且请注意一下自己的形象。&#8221; <br><br>&#8220;嗯？&#8221;，zero 回过神来，发现自己不知什么时候站了起来，而整个餐厅里的人都在看着他嘿嘿偷笑，这让他感觉自己像个傻瓜。 <br><br>zero 红着脸坐下，压低了声音问 Solmyr ：&#8220;不过 Solmyr ，这确实是一个的垃圾收集机制啊，只要我们把这个类改成 &#8230;&#8230; 嗯 &#8230;&#8230; 改成模板类，象这样：&#8221;zero 抓过了纸笔，写到： <br><br>template &lt;typename T&gt; <br>class my_ptr <br>{ <br>private: <br>&nbsp;&nbsp;&nbsp; T* m_p; <br>&nbsp;&nbsp;&nbsp; int* m_count; <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230; <br>}; <br><br>&#8220;它不就能支持任意类型的指针了吗？我们就可以把它用在任何地方。&#8221; <br><br>Solmyr 摇了摇头：&#8220;不，你把问题想的太简单了。对于简单的类型，这个类确实可以处理的很好，但实际情况是很复杂的。考虑一个典型情况：类 Derived 是类 Base 的派生类，我们希望这样赋值：&#8221; <br><br>Base* pb; <br>Derived pd; <br>&#8230;&#8230;&#8230;&#8230; <br>pb = pd; <br><br>&#8220;你倒说说看，这种情况，怎样改用上面这个智能指针来处理？&#8221; <br><br>&#8220;&#8230;&#8230;&#8221;，zero 沉默了。 <br><br>&#8220;要
实现一个完整的垃圾收集机制并不容易，因为有许多细节要考虑。&#8221;，Solmyr
开始总结了，&#8220;不过，基本思路就是上面说的这些。值得庆幸的是，目前已经有了一个相当成熟的&#8216;引用计数&#8217;智能指针，boost::shared_ptr。
大多数情况下，我们都可以使用它。另外，除了智能指针之外，还有一些技术也能够帮助我们避开释放内存的问题，比如内存池。但是，关键在于 ——— &#8221; <br><br>Solmyr 再度用那种平静的眼神盯着 zero ： <br><br>&#8220;身为 C/C++ 程序员，必须有创造力。那种躺在语言机制上不思进取的人，那种必须要靠语法强制才知道怎样编程的人，那种没有别人告诉他该干什么就无所适从的人，不适合这门语言。&#8221;<img src ="http://www.cppblog.com/xmli/aggbug/93796.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 11:05 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93796.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之四：对象计数（下）</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93791.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 02:49:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93791.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93791.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93791.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93791.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93791.html</trackback:ping><description><![CDATA[(续上期） <br>&#8220;空泛的讨论让人厌烦。&#8221;，Solmyr 笑容可掬的说道，&#8220;不如我们设定一个简单的场景来看看你的计数器怎么使用吧。假设你是暴雪的程序员，要为星际争霸设计程序表示神族的单位，那么最简单的方案是 ——&#8221;，Solmyr 停了下来，望向 zero 。 <br><br>zero 松了一口气 —— 这个问题还不算困难。他在脑中整理了一下思路：&#8220;神族的单位应该设计为一个基类，然后每种特定的兵种从这个类派生，每个单位就是这样一个类的对象。&#8221;想到这里，他飞快的在百板上写下： <br><br>class ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>}; <br><br>class Probe : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>}; <br><br>class Zealot : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>}; <br><br>class Dragoon : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>}; <br><br>Solmyr 点了点头，接着说到：&#8220;很好。接下来，我们都知道星际争霸里每个单位都是要占用人口的，也就是说我们得确切知道单位个数，很明显，这是一个对象计数的应用。那么我们该怎样利用你刚才实现的计数器呢？&#8221; <br><br>zero 顺手就在白板上写下： <br><br>class Probe : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;Probe&gt; m_MyCounter; <br>}; <br><br>class Zeolot : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;Zeolot&gt; m_MyCounter; <br>}; <br><br>class Dragoon : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;Dragoon&gt; m_MyCounter; <br>}; <br><br>不对！！ <br><br>zero 心中划过警兆：这感觉太熟悉了！几乎每次惨遭 Solmyr 毒手之前，都有这种感觉！他几乎都可以感受到 Solmyr 正在寻找顺手的东西来砸他。一定有什么地方不对了！ <br><br>回过头来看看自己写下的东西，zero 很快的发现了自己的错误：Counter&lt;Zeolot&gt; 和 Counter&lt;Dragoon&gt; 是不同的类，它们的计数值各自独立，而星际争霸中各兵种占用人口是共享的。 <br><br>&#8220;既然是共享的，那么应该加到基类里。&#8221;，zero 急急忙忙的擦去了上面两行代码，写下： <br><br>class ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;ProtossUnit&gt; m_MyCounter; <br>}; <br><br>还 &#8230;&#8230; 还是不对！zero 立刻又发现了问题：不同的兵种可能占用的人口数并不相同，象 Probe 就只占用一个人口，而 Zealot 和 Dragoon 就要占用两个，这 &#8230;&#8230; 这 &#8230;&#8230; <br><br>zero 再度擦去了刚写下的代码，站在白板之前举棋不定。这时 Solmyr 的声音响了起来：&#8220;怎么了？有困难吗？&#8221;。此时 Solmyr 脸上的笑容显得特别可恶。 <br><br>&#8220;不，我只是不清楚星际争霸中的人口是怎样定义的，这个游戏我从来没有玩过。&#8221;，zero 试图拖延一点时间。 <br><br>&#8220;是吗？昨天我怎么还听到你在讨论&#8216;星际争霸神族战术&#8217;？而且刚才你一下子就写出了三个神族兵种的名称，拼写准确。&#8221;，Solmyr 轻易的戳破了 zero 的谎言。 <br><br>&#8220;&#8230;&#8230;&#8221;，zero 不由得懊恼起来。&#8220;怎么办？得让它们共享一个计数器，而且每种兵种的计数值必须不一样 &#8230;&#8230; 对了！&#8221;zero 脑中灵光一闪，写下如下代码： <br><br>class Probe : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;ProtossUnit&gt; m_MyCounter; <br>}; <br><br>class Zeolot : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;ProtossUnit&gt; m_MyCounter; <br>&nbsp;&nbsp;&nbsp; Counter&lt;ProtossUnit&gt; m_MyCounter; <br>}; <br><br>class Dragoon : public ProtossUnit <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;ProtossUnit&gt; m_MyCounter; <br>&nbsp;&nbsp;&nbsp; Counter&lt;ProtossUnit&gt; m_MyCounter; <br>}; <br><br>&#8220;Yeah！OK 了！&#8221;，zero 高兴的喊道，全然不顾台下带着笑意的目光 —— 这样的有趣场面已经成了公司里的著名娱乐之一。&#8220;共享一个计数器的关键是用哪个类别作为模板参数！不一定非得把本身作为模板参数，完全可以用各个兵种共同的基类！&#8221; <br><br>&#8220;那计数值不是 1 呢？&#8221; <br><br>&#8220;多放几个计数器就行了！&#8221; <br><br>&#8220;嗯，还算不错。&#8221; <br><br>zero 很高兴的看到 Solmyr 上前来在白板上打了一个勾，然而喜悦仅仅维持了一瞬间 —— Solmyr 顺手又在勾上打了一个点。 <br><br>&#8220;为什么打个点？&#8221;，zero 不满的问。 <br><br>&#8220;因为你的计数器设计不佳，想象一下 Carrier ，它占 8 个人口，你是不是要在 Carrier 类中写 8 个 Counter 成员？或者声明一个 Counter 的数组？这样的声明清晰吗？易读吗？&#8221; <br><br>&#8220;呃 &#8230;&#8230;&#8221; <br><br>&#8220;而且这样使用 Counter 成员变量，需要计数的对象在空间上会付出更大的代价，对于小对象，大小甚至可能翻一倍。&#8221; <br><br>&#8220;嗯 &#8230;&#8230;&#8221; <br><br>&#8220;更进一步的说，计数值为 n 的对象，需要构造 n 个 Counter 对象，运行性能也要受影响。&#8221; <br><br>&#8220;啊 &#8230;&#8230; &#8221; <br><br>&#8220;现在你说说看，怎么改进你的计数器，同时不用改动原来的客户代码？&#8221; <br><br>&#8220;哦 &#8230;&#8230; &#8221; <br><br>zero 陷入了沉思：改进后的计数器应该有指定计数值的能力，这个能力应该是 &#8230;&#8230; 应该是对应于一个计数器对象而非整个计数器类的，因为共享同一个计数器的类可能计数值不同，也就是说这里需要为计数器类的对象指定一个参数 &#8230;&#8230; 啊！原来这么简单！ <br><br>&#8220;我知道了！答案就是构造函数！&#8221;，zero 飞快的把计数器类的定义改为（原来定义请参见上一期）： <br><br>template &lt;class T&gt; <br>class Counter <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; Counter(int step) // 改动部分 <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_step = step; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_count += m_step; <br>&nbsp;&nbsp;&nbsp; }; <br>&nbsp;&nbsp;&nbsp; ~Counter(){ m_count -= m_step; }; // 改动部分 <br><br>&nbsp;&nbsp;&nbsp; int GetCout(){ return m_count; }; <br><br>private: <br>&nbsp;&nbsp;&nbsp; static int m_count; <br>&nbsp;&nbsp;&nbsp; int m_step; // 新加部分 <br>}; <br><br>&#8220;嗯，不错，不过还有问题。&#8221;，Solmyr 一边点头一边说，&#8220;这样一来，以前编写的使用 Counter 类的客户代码就不能编译了 —— 它们会报告说构造的时候少了一个参数。&#8221; <br><br>&#8220;这好办。&#8221;，zero 很快发现了自己漏掉了什么。他把构造函数的定义改为： <br><br>Counter(int step = 1) <br><br>&#8220;这样一来，以前的客户代码会缺省的得到计数值 1 ，就像以前一样。&#8221; <br><br>&#8220;嗯，表现不错，不过 &#8230;&#8230;&#8221; <br><br>zero 心中一紧。 <br><br>&#8220;算了，今天就这样吧。&#8221; <br><br>&#8220;Yeah！&#8221; <br><br>&#8220;把今天这些讨论整理成详细文档，下班以前交给我&#8221; <br><br>&#8220;啊！～～～～&#8221; <br><br>&#8230;&#8230;&#8230;&#8230;&#8230;&#8230; <br><br>就这样，再一次的，故事在 zero 的惨叫声中结束了。 <img src ="http://www.cppblog.com/xmli/aggbug/93791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 10:49 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之三：对象计数（上）</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93790.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 02:44:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93790.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93790.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93790.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93790.html</trackback:ping><description><![CDATA[台下的座位已经坐满了，除了 Solmyr 的位子。zero 手足无措的望着那唯一的空位，开始第一百次的哀叹为什么自己会落到这样一个尴尬的位置。仅仅几分钟前，一切都还很正常，直到 &#8230;&#8230;&#8230;&#8230; <br><br>&#8230;&#8230;&#8230;&#8230; <br><br>主持人：&#8220;下一个议程，题为&#8216;对象计数&#8217;的 C++ 编程技术讲座，主讲人是zero。&#8221; <br><br>zero： &#8220;什 &#8230;&#8230; 什么？！等一等，这个讲座不是应该由 Solmyr 主讲吗？！&#8221; <br><br>主持人：&#8220;嗯，原定是由 Solmyr 来讲，不过临时有要事出去了，离开之前他指定你顶替。他没有告诉你吗？&#8221; <br><br>zero： &#8220;他压根没有和我提过！我 &#8230;&#8230; 我什么准备也没做！这怎么行？别开玩笑了？！&#8221; <br><br>主持人：&#8220;你不用谦虚，Solmyr 临走前对我说过你完全能够胜任这个议题。啊对了，这里有一张他留给你的条子。&#8221; <br><br>zero 打开条子，但见上面写到：&#8220;《<a  href="http://www.fifid.com/search/50+%E8%AF%AB?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366">50 诫</font></a>》（注：指《More Effective C++ 2/e》一书）看得怎么样了？如果你认真看过，就没问题。如果你敢拒绝或者出了岔子，嘿嘿 &#8230;&#8230;&#8221; <br><br>&#8230;&#8230;&#8230;&#8230; <br><br>&#8220;唉！&#8221;，zero
认命的叹了口气，&#8220;面对现实，硬着头皮上吧！&#8221;他决定就讲最简单的那部分，反正把这个场面搪塞过去就行了。他望着白板上&#8220;对象计数&#8221;四个大字，开口说到：
&#8220;今天 &#8230;&#8230; 这个 &#8230;&#8230; 今天讨论的议题是&#8216;对象计数&#8217;。所谓对象计数 &#8230;&#8230; 啊 &#8230;&#8230; 就是对计算某个类有多少个对象&#8221;。 <br><br>开场白糟透了，zero 觉得还是尽快转入实际的东西比较好。 <br><br>&#8220;对于这个问题 &#8230;&#8230; 最简单的做法是在需要计数的类中添加一个静态变量，保存当前的对象个数，并利用构造函数和析构函数增减它的值，象这样：&#8221; <br><br>class Wedget <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; Wedget(){ m_count++; }; <br>&nbsp;&nbsp;&nbsp; ~Wedget(){ m_count--; }; <br><br>&nbsp;&nbsp;&nbsp; int GetCout(){ return m_count; }; <br><br>private: <br>&nbsp;&nbsp;&nbsp; static int m_count; <br>}; <br><br>int Wedget::m_count = 0; <br><br>说着说着，zero 发现这件事似乎其实没有那么困难，反而觉得渐渐进入了状态，话也流利起来： <br><br>&#8220;上述做法很容易理解：一个类中的 static
类型的成员变量是被这个类的所有对象所共享的。当该类新增一个对象时，构造函数会保证计数值加一，销毁一个对象时，析构函数会保证计数值减一。这里唯一需
要注意的只有一点：如果 Wedget
派生自一个基类，那么基类的析构函数一定得声明为虚函数。为什么呢？因为我们时常会用基类的指针操作派生类的对象，这是所谓&#8220;多态&#8221;的做法，面向对象程序
设计的基本技术之一。也就是说下面这一类的代码会很常见：&#8221; <br><br>class Base <br>&#8230;&#8230; <br><br>class Wedget : public Base <br>&#8230;&#8230; <br><br>Base* pb = new Wedget; // 基类指针指向派生类对象 <br>&#8230;&#8230; <br><br>delete pb; <br><br>&#8220;但如果 Base 的析构函数没有声明为虚函数，那么当执行到 delete pb 这一句的时候，编译器只知道 pb 是一个 Base*
类型的指针，只会去调用 Base 类的析构函数，这样一来，明明销毁了一个 Wedget 类的对象，Wedget
类的析构函数却没有调用，计数值就会出现错误。所以必须将 Base 的析构函数声明为虚，告诉编译器去判断这个对象的实际类型，保证 Wedget
类的析构函数被调用。&#8221; <br><br>zero 顿了一顿，续道： <br><br>&#8220;顺便指出一下，这一点是 C++ 面向对象程序设计的一个普遍原则。&#8221; <br><br>zero 环视了一眼台下，发现所有人都听的很认真，有些人还露出了领悟的表情，这使得他信心大增，决定接着讲下去： <br><br>&#8220;某种意义上说，现在我们已经解决了&#8216;对象计数&#8217;这个问题。但是事情还没完 ——
我们可能有许多类都需要对对象计数，如果我们对每个类都象上面这样手工的添这些代码进去，那么这个工作既枯燥乏味又容易出错，因此我们需要一种通用的机制。最简单的，当然是把上面的代码封装成一个类：&#8221; <br><br>class Counter <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; Counter(){ m_count++; }; <br>&nbsp;&nbsp;&nbsp; ~Counter(){ m_count--; }; <br><br>&nbsp;&nbsp;&nbsp; int GetCout(){ return m_count; }; <br><br>private: <br>&nbsp;&nbsp;&nbsp; static int m_count; <br>}; <br><br>int Counter::m_count = 0; <br><br>&#8220;然后在那些需要计数的类中添加一个 Counter 的成员，象这样：&#8221; <br><br>class Wedget <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter m_MyCounter; <br>}; <br><br>&#8220;这
样一来，新增一个 Wedget 对象也就新增一个 Counter 对象，销毁一个 Wedget 对象也就销毁一个 Counter
对象，看上去很完美。但是 &#8230;&#8230;&#8221;，zero 拖了个长音，&#8220;这样的解法是错误的！&#8221;说完，zero 在白板上夸张的打了一个大叉。 <br><br>看到台下人们疑惑的表情，zero 对自己行为戏剧性的效果感到非常满意，他得意洋洋的解释： <br><br>&#8220;因为 static 成员是被该类所有的对象共享的，所以如果有另一个类，比如 Other 类也为了进行计数而包含了一个 m_MyCounter
成员的话，那么 Wedget 和 Other 类实际上是在共享一个计数值！请注意，Wedget 的 m_MyCounter 成员和 Other
的 m_MyCounter 成员都是 Counter 类的对象，它们共享同一个 m_count 静态变量。&#8221; <br><br>&#8220;OK，要绕开这个问题，必须用一点点小手段，那就是模板：&#8221;，zero 在白板上写出如下的代码： <br><br>template &lt;class T&gt; <br>class Counter <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; Counter(){ m_count++; }; <br>&nbsp;&nbsp;&nbsp; ~Counter(){ m_count--; }; <br><br>&nbsp;&nbsp;&nbsp; int GetCout(){ return m_count; }; <br><br>private: <br>&nbsp;&nbsp;&nbsp; static int m_count; <br>}; <br><br>template &lt;class T&gt; <br>int Counter&lt;T&gt;::m_count = 0; <br><br>class Wedget <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;Wedget&gt; m_MyCounter; <br>}; <br><br>class Other <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230; <br>&nbsp;&nbsp;&nbsp; Counter&lt;Other&gt; m_MyCounter; <br>}; <br><br>&#8220;看出其中的区别了吗？Counter&lt;Wedget&gt; 和 Counter&lt;Other&gt; 是两个类，因此它们的 m_count 各自独立，就这样，我们实现了不同的类各自独立计数。&#8221; <br><br>zero
一转身，惊讶的看到 Solmyr 不知什么时候已经出现在他座位上了，嘴边带着 —— 什么？没看错吧？zero 发现那不是 Solmyr
招牌式的坏笑，而是一种支持、赞许的微笑，zero 简直不能相信自己的眼睛。不过一转眼，Solmyr 的表情再度切换回了 zero 熟悉的模式
—— 快的让人以为刚才所看到的根本是幻觉 —— zero 心中一沉，知道事情有些不妙了，果然 —— <br><br>&#8220;我来提个问题。&#8221;，Solmyr 发话了，而且笑的很灿烂 &#8230;&#8230; <img src ="http://www.cppblog.com/xmli/aggbug/93790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 10:44 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Solmyr 的小品文系列之二：模棱两可的陷阱</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93788.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 02:39:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93788.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93788.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93788.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93788.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93788.html</trackback:ping><description><![CDATA[<meta http-equiv="CONTENT-TYPE" content="text/html; charset="utf-8"">
<title></title>
<meta name="GENERATOR" content="OpenOffice.org 3.1  (Win32)"><style type="text/css">
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
A:link { so-language: zxx }
-->
</style>
<p style="margin-bottom: 0cm;" align="LEFT">&#8220;<span lang="zh-CN">为什么会这样？！&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">一边喝水一边嘟囔着，恨恨的看着面前显示器上的代码，&#8220;为什么这么简单的一个调用也会出现编译错误
&#8230;&#8230; &#8221; </span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">这是因为你的设计太差！&#8221;
</span><font face="Times New Roman, serif"><br><br></font><span lang="zh-CN">噗！</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">被幽灵一样出现在背后的 </span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">吓了一大跳，一口水差点全喷出来。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">咳！咳咳！</span><font face="Times New Roman, serif">S
&#8230;&#8230; Solmyr </font><span lang="zh-CN">，你什么时候站在我背后的？&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">很费力的平息了咳嗽，同时努力回想刚才自己有没有把柄会被
</span><font face="Times New Roman, serif">Solmyr </font><span lang="zh-CN">抓到。
</span><font face="Times New Roman, serif"><br><br>Solmyr
</font><span lang="zh-CN">抓过一张椅子坐了下来：&#8220;在你一开始干傻事的时候我就在了，正是这个糟糕的设计导致了现在困扰你的编译错误。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">哪
&#8230;&#8230; 哪里？&#8221; </span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">这儿。&#8221;
</span><font face="Times New Roman, serif">Solmyr </font><span lang="zh-CN">抓过键盘，标出了下面这段代码：
</span><font face="Times New Roman, serif"><br><br>void SomeFunc(int
i) <br>&#8230;&#8230;&#8230;&#8230; <br><br>void SomeFunc(float f) <br>&#8230;&#8230;&#8230;&#8230;
<br><br>int main(void) <br>{ <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230;
<br>&nbsp;&nbsp;&nbsp; SomeFunc(1.2);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Error! ambiguous call
<br>&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230; <br>} <br><br>&#8220;</font><span lang="zh-CN">我
也正觉得奇怪&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">一如既往的挠着头，试图压榨不存在的智慧，&#8220;这么简单的一个函数重载，应该很清楚才对。我这里调用时明明给出的是浮点数，显然应该调用
</span><font face="Times New Roman, serif">float </font><span lang="zh-CN">版本的
</span><font face="Times New Roman, serif">SomeFunc
</font><span lang="zh-CN">。最奇怪的是如果没有这个调用，整个程序编译连接完全没有问题，可见这样重载函数是合法的。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">嗯，没错，确实是合法的，但是合法不代表正确。</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">，你念一下这一段，看看先知
</span><font face="Times New Roman, serif">Meyers </font><span lang="zh-CN">在他的《</span><font face="Times New Roman, serif"><a  href="http://www.fifid.com/search/50+%E8%AF%AB?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366">50
</font></a></font><a  href="http://www.fifid.com/search/50+%E8%AF%AB?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366"><span lang="zh-CN">诫</span></font></a><span lang="zh-CN">》（注：指《</span><font face="Times New Roman, serif">Effective
C++ 2/e</font><span lang="zh-CN">》一书）中的条款 </span><font face="Times New Roman, serif">26
</font><span lang="zh-CN">中是怎样描述 </span><font face="Times New Roman, serif">C++
</font><span lang="zh-CN">对待&#8216;模棱两可&#8217;的哲学的。&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">翻开了一本书，指着其中的几行。
</span><font face="Times New Roman, serif"><br><br>&#8220;C++ &#8230;&#8230;&#8221;
<br><br>&#8220;</font><span lang="zh-CN">站起来，大声念！&#8221;
</span><font face="Times New Roman, serif"><br><br>zero
</font><span lang="zh-CN">依言站起，中气十足的念道：&#8220;</span><font face="Times New Roman, serif">C++
</font><span lang="zh-CN">也有一个哲学信仰：它相信潜在的模棱两可的状态不是一种错误。&#8221;
</span><font face="Times New Roman, serif"><br><br></font><span lang="zh-CN">旁边的座位上传来低低的窃笑声，更远处的人探头张望，投来好奇的目光，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">顿时感到自己像个傻瓜。当 </span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">看到 </span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">嘴边招牌式的坏笑时明白了过来：自己又一次被
</span><font face="Times New Roman, serif">Solmyr </font><span lang="zh-CN">设计了。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">嗯，明白了这一点，我们就可以展开进一步的讨论了&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">开始转入正题，&#8220;还记得上次我说过上面的
</span><font face="Times New Roman, serif">1.2 </font><span lang="zh-CN">是什么吗？&#8221;
</span><font face="Times New Roman, serif"><br><br>zero </font><span lang="zh-CN">露出了回忆的表情：&#8220;嗯
&#8230;&#8230; </span><font face="Times New Roman, serif">1.2 </font><span lang="zh-CN">是&#8216;写在代码里的常量&#8217;&#8230;&#8230;
应该是一个 </span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">类型常量。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">这就是问题所在：编译器看到这个调用函数的请求，会去寻找你的重载函数中哪个函数能够匹配这个调用请求给出的参数，结果它发现没有一个函数的参数是
</span><font face="Times New Roman, serif">double </font><span lang="zh-CN">类型的，所以必须要做类型转换，但是
</span><font face="Times New Roman, serif">double </font><span lang="zh-CN">类型既可以转成
</span><font face="Times New Roman, serif">int </font><span lang="zh-CN">，也可以转成
</span><font face="Times New Roman, serif">float
</font><span lang="zh-CN">，究竟转哪个好呢？编译器不知道，所以只好报错了。明白了吗？&#8221;
</span><font face="Times New Roman, serif"><br><br>zero </font><span lang="zh-CN">似懂非懂的点了点头。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">那我问你，这样重载编译时会不会报错？&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">稍稍改动了一下 </span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">的代码： </span><font face="Times New Roman, serif"><br><br>void
SomeFunc(int i) <br>&#8230;&#8230;&#8230;&#8230; <br><br>void SomeFunc(double db)
<br>&#8230;&#8230;&#8230;&#8230; <br><br>int main() <br>{ <br>&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230;
<br>&nbsp;&nbsp;&nbsp; float f = 1.2; <br>&nbsp;&nbsp;&nbsp; SomeFunc(f); <br>&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230;
<br>} <br><br>zero </font><span lang="zh-CN">看了看，学着
</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">的语气说到：&#8220;编译器发现没有一个函数的参数是
</span><font face="Times New Roman, serif">float </font><span lang="zh-CN">类型的，所以必须要做类型转换，但是
</span><font face="Times New Roman, serif">float </font><span lang="zh-CN">类型既可以转成
</span><font face="Times New Roman, serif">int </font><span lang="zh-CN">，也可以转成
</span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">，究竟转哪个好呢？编译器不知道，所以只好报错了。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">错！&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">顺手按下了运行按钮，程序运行一切正常，输出显示调用的是
</span><font face="Times New Roman, serif">double </font><span lang="zh-CN">版本的
</span><font face="Times New Roman, serif">SomeFunc </font><span lang="zh-CN">函数。
</span><font face="Times New Roman, serif"><br><br>zero
</font><span lang="zh-CN">再度感到了困惑：&#8220;为什么同样是要选择类型转换，这个就没错，前一个就有错呢？这中间的逻辑何在？&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">重要的是这一句：&#8216;究竟转哪个好呢？编译器不知道&#8217;。你没有注意到我说这句话的时候&#8216;好&#8217;字上用了一个重音吗？&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">你用过重音吗？&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220; &#8230;&#8230;
</font><span lang="zh-CN">这个不是重点。重点在于，</span><font face="Times New Roman, serif">float
</font><span lang="zh-CN">到 </span><font face="Times New Roman, serif">int
</font><span lang="zh-CN">和 </span><font face="Times New Roman, serif">float
</font><span lang="zh-CN">到 </span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">这两个转换，编译器是能够选择的，因为
</span><font face="Times New Roman, serif">float </font><span lang="zh-CN">到
</span><font face="Times New Roman, serif">int </font><span lang="zh-CN">会损失数据
—— 象样的编译器会在做这种类型转换的时候给出一个
</span><font face="Times New Roman, serif">warning —— </font><span lang="zh-CN">而
</span><font face="Times New Roman, serif">float </font><span lang="zh-CN">到
</span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">则不损失数据，所以编译器知道&#8216;转哪个好&#8217;。而之前的情况，</span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">到 </span><font face="Times New Roman, serif">int
</font><span lang="zh-CN">到 </span><font face="Times New Roman, serif">float
</font><span lang="zh-CN">的转换都要损失数据，所以编译器不知道&#8216;转哪个好&#8217;，它没办法做一个决定
—— &#8221;，</span><font face="Times New Roman, serif">Solmyr </font><span lang="zh-CN">看了看
</span><font face="Times New Roman, serif">zero </font><span lang="zh-CN">，再度问道，&#8220;明白了吗？&#8221;
</span><font face="Times New Roman, serif"><br><br>zero
</font><span lang="zh-CN">皱着眉头，挠头挠的更起劲了，显然对于消化一下子出现的这么多信息感到少许困难：&#8220;我想我明白了，关键是编译器能否区分两个类型转换。在这里区分的关键是
类型转换是否损失数据，嗯 &#8230;&#8230; 所以我只要在所有用到浮点数的场合都使用
</span><font face="Times New Roman, serif">double </font><span lang="zh-CN">类型，就不会有问题，即使别人用
</span><font face="Times New Roman, serif">float </font><span lang="zh-CN">来调用也一样。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">正确。不过&#8216;模棱两可&#8217;的问题可不仅仅出现浮点数身上，例如，这样两个重载函数
&#8230;&#8230; &#8221;，</span><font face="Times New Roman, serif">Solmyr </font><span lang="zh-CN">接着键入：
</span><font face="Times New Roman, serif"><br><br>void
SomeFunc(double db) <br>void SomeFunc(char ch)
<br><br>&#8220;</font><span lang="zh-CN">如果我用一个整形变量来调用，会出现什么事情？&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">扭头盯着 </span><font face="Times New Roman, serif">zero</font><span lang="zh-CN">。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">呃
&#8230;&#8230; 编译器同样无法区分 </span><font face="Times New Roman, serif">int
</font><span lang="zh-CN">到 </span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">和 </span><font face="Times New Roman, serif">int
</font><span lang="zh-CN">到 </span><font face="Times New Roman, serif">char
</font><span lang="zh-CN">这两个类型转换，所以同样会报错。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">正确。你能够自己举出几个例子吗？&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">把键盘递了回去。 </span><font face="Times New Roman, serif"><br><br></font><span lang="zh-CN">很明显的，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">陷入了沉思，过了一会儿，屏幕上出现了这样几行代码：
</span><font face="Times New Roman, serif"><br><br>// </font><span lang="zh-CN">用
</span><font face="Times New Roman, serif">int </font><span lang="zh-CN">调用的话会出错
</span><font face="Times New Roman, serif"><br>void fun(char ch)
<br>void fun(int* pi)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // </font><span lang="zh-CN">或者其他指针
</span><font face="Times New Roman, serif"><br><br>// </font><span lang="zh-CN">用
</span><font face="Times New Roman, serif">int </font><span lang="zh-CN">调用同样会出错
</span><font face="Times New Roman, serif"><br>void fun(double db)
<br>void fun(int* pi)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // </font><span lang="zh-CN">或者其他指针
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">嗯，很好。不过你还是漏了一种重要情况，&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">补充道，&#8220;就是参数有缺省值的时候：&#8221;
</span><font face="Times New Roman, serif"><br><br>// </font><span lang="zh-CN">调用时如果不给参数会出错
</span><font face="Times New Roman, serif"><br>void fun(int i=10)
<br>void fun() <br><br>&#8220;</font><span lang="zh-CN">天哪！&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">看起来快要崩溃了，&#8220;居然有这么多模棱两可的陷阱，这叫我怎样发布我的函数？在文档里写：以下
</span><font face="Times New Roman, serif">153 </font><span lang="zh-CN">种调用方式将导致编译错误吗？&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">不要这么紧张，&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">好整以暇的说到，&#8220;重载函数的模棱两可现象不是不能避免的，办法有两个：一是用模板来代替重载，尤其是象你的
</span><font face="Times New Roman, serif">SomeFunc </font><span lang="zh-CN">这样
</span><font face="Times New Roman, serif">int </font><span lang="zh-CN">型和
</span><font face="Times New Roman, serif">double
</font><span lang="zh-CN">型处理算法相同的情况；二是如果要用重载的话，尽可能保证函数的参数个数不同。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">可是如果处理算法不一样，函数需要的参数个数又相同，那该怎么办？&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">很简单，加入&#8216;无用的参数&#8217;，象这样：&#8221;
</span><font face="Times New Roman, serif"><br><br>void
SomeFunc(float db, int) <br>void SomeFunc(int i)
<br><br>&#8220;</font><span lang="zh-CN">第一个函数的第二个参数没有任何作用，所以你可以干脆不给它命名，只要声明一下有这个
</span><font face="Times New Roman, serif">int
</font><span lang="zh-CN">型参数就可以了。文档里可以这样写：该参数是为今后升级预留的余地，调用时请传入
</span><font face="Times New Roman, serif">0 </font><span lang="zh-CN">值。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220; &#8230;&#8230;
</font><span lang="zh-CN">你的文档里大概都是这一类的话吧
&#8230;&#8230; 啊！好痛！这回又是一本书！&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">被 </span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">突如其来的袭击击中，发出了悲惨的哀鸣。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">你得感谢先知
</span><font face="Times New Roman, serif">Scott Meyers</font><span lang="zh-CN">，他的《</span><a  href="http://www.fifid.com/search/+50+%E8%AF%AB?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366"><span lang="zh-CN">
</span><font face="Times New Roman, serif">50 </font></font></a><font color="#003366"></font><a  href="http://www.fifid.com/search/+50+%E8%AF%AB?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366"><span lang="zh-CN">诫</span></font></a><span lang="zh-CN">》轻而薄，我手上拿的若是一本教主
</span><font face="Times New Roman, serif">Bjarne Stroustrup
</font><span lang="zh-CN">的《</span><a  href="http://www.fifid.com/search/%E5%9C%A3%E7%BB%8F?src=yb_qsal&amp;utm_source=yb_qsal&amp;utm_medium=link&amp;utm_content=post" target="_blank"><font color="#003366"><span lang="zh-CN">圣经</span></font></a><span lang="zh-CN">》（注：指《</span><font face="Times New Roman, serif">The
C++ Programing Language 3/e</font><span lang="zh-CN">》一书，</span><font face="Times New Roman, serif">Bjarne
Stroustrup </font><span lang="zh-CN">是 </span><font face="Times New Roman, serif">C++
</font><span lang="zh-CN">语言的设计者），你现在已经爬不起来了。&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">再度披上了修养的伪装，不过言辞中仍然留着一点点杀气的痕迹
&#8230;&#8230; </span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">真是残暴的家伙
&#8230;&#8230;&#8221;，</span><font face="Times New Roman, serif">zero </font><span lang="zh-CN">小声嘟囔着。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">你说什么？&#8221;，杀气再度升高。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">不，
不！我什么也没说！&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">连忙否认，试着转移话题，&#8220;啊！我懂了，要避免模棱两可的陷阱，一是用模板来替代重载，二是利用加入&#8216;无用的参数&#8217;这一手段保证重载函数参数个数不同。这
样就可以避开模棱两可的问题，是不是，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">老师？&#8221;。</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">很努力的装出天真无邪的样子。
</span><font face="Times New Roman, serif"><br><br>&#8220; &#8230;&#8230; </font><span lang="zh-CN">真是拙劣的演技
&#8230;&#8230; &#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">心中暗想。&#8220;不完全，上述手段只能解决函数重载这一块而已，模棱两可问题涉及的情况要广泛的多，比如《
</span><font face="Times New Roman, serif">50 </font><span lang="zh-CN">诫》中的例子：&#8221;
</span><font face="Times New Roman, serif"><br><br>class B;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //
</font><span lang="zh-CN">前置声明 </span><font face="Times New Roman, serif"><br><br>class
A <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; A(const B&amp;);&nbsp;&nbsp; // A </font><span lang="zh-CN">可以根据
</span><font face="Times New Roman, serif">B </font><span lang="zh-CN">构造出来
</span><font face="Times New Roman, serif"><br>}; <br><br>class B <br>{
<br>public: <br>&nbsp;&nbsp;&nbsp;&nbsp; operator A() const;&nbsp;&nbsp;&nbsp; // B </font><span lang="zh-CN">可以被转换为
</span><font face="Times New Roman, serif">A <br>};
<br><br>&#8220;</font><span lang="zh-CN">这两个类本身没有什么问题，但若是有个函数需要
</span><font face="Times New Roman, serif">A </font><span lang="zh-CN">的对象作为参数，传过去的却是个
</span><font face="Times New Roman, serif">B </font><span lang="zh-CN">的对象时：&#8221;
</span><font face="Times New Roman, serif"><br><br>void f(const A&amp;)
<br>B b; <br>f(b);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Error! ambiguous call <br><br>&#8220;</font><span lang="zh-CN">注
意到这里面的问题了吗？有两种一样好方法可以完成转换，一是用
</span><font face="Times New Roman, serif">A </font><span lang="zh-CN">的构造函数以
</span><font face="Times New Roman, serif">b </font><span lang="zh-CN">为参数构造一个新的
</span><font face="Times New Roman, serif">A </font><span lang="zh-CN">类对象，而是调用
</span><font face="Times New Roman, serif">B </font><span lang="zh-CN">的转换函数将
</span><font face="Times New Roman, serif">b </font><span lang="zh-CN">转换为一个
</span><font face="Times New Roman, serif">A
</font><span lang="zh-CN">类对象。编译器再度无法区分哪个转换更好，只能报错了。后面还有一个多重继承的例子，你自己看吧&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">这
&#8230;&#8230; 这 &#8230;&#8230;&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">刚刚建立起来的对回避陷阱的自信再度崩塌。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">要回避一切模棱两可的问题是不可能的，&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">站起身来，&#8220;关键是了解它为什么会发生，怎样的情况容易诱发它，然后小心的加以处理，</span><font face="Times New Roman, serif">C++
</font><span lang="zh-CN">中很多问题都是如此。这块《 </span><font face="Times New Roman, serif">50
</font><span lang="zh-CN">诫》的石板就留给你了，好好研读吧。哈哈哈哈！&#8221;，</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">一边笑着一边离开了 </span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">，背影看起来像是一位飘然远去的高人
&#8230;&#8230; </span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">什么呀！根本就只是一个性格残暴的家伙而已，装模做样
&#8230;&#8230; 啪！&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">话音未落，一个文件夹划破空气飞来，正中
</span><font face="Times New Roman, serif">zero </font><span lang="zh-CN">的面门。
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">呜
～ 我什么也没说 ～&#8221;，</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">无力的辨白，然而换来的只是旁边的座位上再度传来低低的窃笑声而已。</span><font face="Times New Roman, serif">zero
</font><span lang="zh-CN">明白，今天他的形象算是彻底的毁了
&#8230;&#8230; </span>
</p><img src ="http://www.cppblog.com/xmli/aggbug/93788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2009-08-19 10:39 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>