﻿<?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/category/8207.html</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>Sat, 11 Jun 2011 08:47:33 GMT</lastBuildDate><pubDate>Sat, 11 Jun 2011 08:47:33 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>0</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>3</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>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>0</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>一个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>如何保持软件开发团队的稳定性  （转）</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>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><item><title>Solmyr 的小品文系列之一：字符串放在哪里？</title><link>http://www.cppblog.com/xmli/archive/2009/08/19/93784.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Aug 2009 02:24:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/08/19/93784.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/93784.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/08/19/93784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/93784.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/93784.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="LEFT"><span lang="zh-CN">画外音：今天是个大晴天，温暖的阳光透过窗子照进了这间宽敞的办公室，办公室里三三两两的人们正在各自的计算机前努力工作，一切都显得那么的安静、祥和、有条不紊
&#8230;&#8230; </span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">啊～！救命啊！</span><font face="Times New Roman, serif">Solmyr
</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></font><span lang="zh-CN">画外音：
&#8230;&#8230; 呃，好吧，我得承认有点小小的例外。这里是一家软件公司，发出惨叫的这位是
</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">zero </font><span lang="zh-CN">这一批新人的培训。啊，故事开始了
&#8230;&#8230; </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;这次你拿来砸我的文件夹又大了一号！&#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">zero
</font><span lang="zh-CN">刚刚交上来的一段代码： </span><font face="Times New Roman, serif"><br><br>&#8230;&#8230;
<br>char* msg = &#8220;Connectting ... Please wait&#8220; <br>&#8230;&#8230; <br>if(
Status == S_CONNECTED ) <br>	strcpy(msg, &#8220;Connectted&#8220;); <br>&#8230;&#8230;
<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"><br><br>&#8220;</font><span lang="zh-CN">你看不出来吗？</span><font face="Times New Roman, serif">connect
</font><span lang="zh-CN">这个单词的进行时和过去时你都拼错了，多打了一个
</span><font face="Times New Roman, serif">t&#8221;</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">就为了这个你又用文件夹砸我
&#8230;&#8230; 啊！这次又是光盘盒！&#8221; </span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">这是商用软件，你以为是在
</span><font face="Times New Roman, serif">QQ </font><span lang="zh-CN">上和
</span><font face="Times New Roman, serif">PPMM
</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">处于爆发的边缘，&#8220;好吧，让我们从基础开始，</span><font face="Times New Roman, serif">C
</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;</span><font face="Times New Roman, serif">C/C++
</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;</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">zero
</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">msg </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"><br><br>&#8220;</font><span lang="zh-CN">这个
&#8230;&#8230; 呃 &#8230;&#8230; 我想 &#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"><br><br>&#8220;&#8230;&#8230;&#8221;
<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">Solmyr
</font><span lang="zh-CN">飞快的键入了如下代码： </span><font face="Times New Roman, serif"><br><br>char
msg[] = &#8220;Hello&#8220;; <br><br>char* pmsg = (char*)malloc(
sizeof(&#8220;Hello&#8220;) ); <br>strcpy(pmsg, &#8220;Hello&#8220;);
<br><br>&#8220;</font><span lang="zh-CN">上面这些代码你应该都很清楚了：</span><font face="Times New Roman, serif">msg
</font><span lang="zh-CN">是一个字符数组，</span><font face="Times New Roman, serif">C
</font><span lang="zh-CN">语言保证会为它分配一段连续的内存，并将其初始化为
&#8220;</span><font face="Times New Roman, serif">Hello&#8220; </font><span lang="zh-CN">。</span><font face="Times New Roman, serif">pmsg
</font><span lang="zh-CN">是一个字符指针，我们调用了
</span><font face="Times New Roman, serif">malloc </font><span lang="zh-CN">函数为它动态分配了一块内存，并用
</span><font face="Times New Roman, serif">strcpy </font><span lang="zh-CN">函数填充其值为
&#8220;</span><font face="Times New Roman, serif">Hello&#8220;
</font><span lang="zh-CN">。这两种做法的共通点是：首先用正常手段获得一段内存，然后填充值。接着再来看这个：&#8221;
</span><font face="Times New Roman, serif"><br><br>char* msg =
&#8220;Hello&#8220;; <br><br>&#8220;</font><span lang="zh-CN">这一句代表什么意思？首先
</span><font face="Times New Roman, serif">msg </font><span lang="zh-CN">是个指针，</span><font face="Times New Roman, serif">C/C++
</font><span lang="zh-CN">语言不负责为它分配一块内存；其次我们也没有显式的为它分配一块内存。它指向哪里？指向
&#8220;</span><font face="Times New Roman, serif">Hello&#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">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"><br><br>double
db = 1.5; <br><br>&#8220;</font><span lang="zh-CN">这 一行里面，</span><font face="Times New Roman, serif">1.5
</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">C/C++
</font><span lang="zh-CN">语言要处理它们，也要分配内存来存放这些东西。同理，当你在代码里写了
&#8220;</span><font face="Times New Roman, serif">Hello&#8220; </font><span lang="zh-CN">，实际上
</span><font face="Times New Roman, serif">C/C++
</font><span lang="zh-CN">语言就分配了一块内存存放这个字符串，当你写
</span><font face="Times New Roman, serif">char* msg = &#8220;Hello&#8220;
</font><span lang="zh-CN">的时候，你就是把这样一块内存的地址赋给了指针
</span><font face="Times New Roman, serif">msg </font><span lang="zh-CN">。所以
</span><font face="Times New Roman, serif">msg
</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">char*&#8221;</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">错！应该是
</span><font face="Times New Roman, serif">const char*
</font><span lang="zh-CN">。想当然耳，写在程序中的字符串你不希望它发生变化，所以很明显的，这块内存应该被解释为常量。但是你在声明
</span><font face="Times New Roman, serif">msg </font><span lang="zh-CN">的时候做了什么？&#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">zero
</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">strcpy
</font><span lang="zh-CN">，往里写了内容。在我们的编译器上，这么做会引发什么后果？&#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">部分正确。准确的讲，只有在工程编译选项为调试版本的时候，如果工程编译选项为发布版本，一切都很正常
—— 奇怪吗？并不，记住这一点：</span><font face="Times New Roman, serif">C/C++
</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">一眼，&#8220;将会是很难发现的。&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8220;</font><span lang="zh-CN">可是说来说去这么做还是没有什么危害不是吗？</span><font face="Times New Roman, serif">msg
</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>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">Solmyr
</font><span lang="zh-CN">面无表情 —— 如果忽略他嘴角那一丝坏笑的话
—— 的说到，&#8220;没有危害是吗？看看下面的代码：&#8221;
</span><font face="Times New Roman, serif"><br><br>char* str1 =
&#8220;Hello&#8220;; <br>char* str2 = &#8220;Hello&#8220;; <br>*str1 = &#8216;P&#8216;; <br>cout
&lt;&lt; str2 &lt;&lt; endl; <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>&#8220;</font><span lang="zh-CN">这还用问吗？当然是输出
</span><font face="Times New Roman, serif">Hello </font><span lang="zh-CN">了。&#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">Solmyr
</font><span lang="zh-CN">按下了运行按钮，屏幕显示的居然是
</span><font face="Times New Roman, serif">Pello </font><span lang="zh-CN">！。
</span><font face="Times New Roman, serif"><br><br>zero
</font><span lang="zh-CN">大为诧异，挠着头试图找出其中的逻辑，突然间灵光一闪：&#8220;我明白了！</span><font face="Times New Roman, serif">str1
</font><span lang="zh-CN">和 </span><font face="Times New Roman, serif">str2
</font><span lang="zh-CN">实际指向同一段内存！因为 </span><font face="Times New Roman, serif">C/C++
</font><span lang="zh-CN">语言在处理 </span><font face="Times New Roman, serif">Hello
</font><span lang="zh-CN">字符串的时候把它当作常量，所以就做了优化，只保存了一份
</span><font face="Times New Roman, serif">Hello </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>&#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/C++
</font><span lang="zh-CN">语言的做法，是这个编译器的做法。简单的说，你如果要对这种字符串写的话，其结果如何，是没有定义的。所谓没有定义，就是
</span><font face="Times New Roman, serif">C/C++
</font><span lang="zh-CN">语言不保证会得到怎样的结果，可能这样也可能样，完全决定于你的编译器作者怎么想。想想看吧，哪天你的程序出现了古怪的问题
—— 比如显示信息出现了混乱 ——
起因却是你在无关的地方写了一个字符串，会怎样？这是维护时最大的恶梦之一。现在你明白危害在哪里了？&#8221;
</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">知道了还不快去改！&#8221;
</span><font face="Times New Roman, serif"><br><br>&#8230;&#8230; <br><br>zero
</font><span lang="zh-CN">跑回坐位修改他的程序去了，办公室里再度恢复了宁静，所有的人都埋头于他们的工作之中。只有
</span><font face="Times New Roman, serif">Solmyr
</font><span lang="zh-CN">一边喝着咖啡一边揉着太阳穴，喃喃地吐出不祥的词句：&#8220;这样的日子才刚刚开始啊
&#8230;&#8230;&#8221; </span>
</p><img src ="http://www.cppblog.com/xmli/aggbug/93784.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:24 <a href="http://www.cppblog.com/xmli/archive/2009/08/19/93784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文件夹选项更改后刷新的问题</title><link>http://www.cppblog.com/xmli/archive/2009/07/03/89152.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 03 Jul 2009 05:03:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/07/03/89152.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/89152.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/07/03/89152.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/89152.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/89152.html</trackback:ping><description><![CDATA[感谢coolslob的帮助， 我被这个问题困惑很久的，今天终于找到的合适的解决方案<br><br>参考链接如下：<br><br>三问文件夹选项更改后刷新的问题<br><a>http://topic.csdn.net/u/20081121/11/f5034a2f-26ae-4f74-a357-1fbd33576883.html </a><br>&nbsp;<br>再问文件夹选项更改后刷新的问题 <br>http://topic.csdn.net/u/20081108/18/ab4009f0-bfd1-4af6-873d-06ec7837236a.html <br>&nbsp;<br>&nbsp;在程序中怎么调用设置文件夹选项的功能？ <br>http://topic.csdn.net/u/20081104/11/b79c58c2-7887-4144-87d0-e9486685cb87.html <br><br>实现代码如下：<br><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: #0000ff;">bool</span><span style="color: #000000;">&nbsp;ShowAllFilesInExplorer(</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;isShowHidden</span><span style="color: #008000;">/*</span><span style="color: #008000;">对应[显示所有的文件和文件夹]</span><span style="color: #008000;">*/</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;isShowSysProtected</span><span style="color: #008000;">/*</span><span style="color: #008000;">对应[显示受操作系统保护的文件]</span><span style="color: #008000;">*/</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;isShowFileExt</span><span style="color: #008000;">/*</span><span style="color: #008000;">对应[隐藏已知文件类型的扩展名]</span><span style="color: #008000;">*/</span><span style="color: #000000;">)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;HKEY&nbsp;hKey&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;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;bRet&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(ERROR_SUCCESS&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;::RegOpenKeyEx(HKEY_CURRENT_USER,&nbsp;TEXT(</span><span style="color: #000000;">"</span><span style="color: #000000;">Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced</span><span style="color: #000000;">"</span><span style="color: #000000;">),&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;KEY_SET_VALUE,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">hKey))<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;dwShowHidden&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;isShowHidden&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;:&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;dwShowSysProtected&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;isShowSysProtected&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;:&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;dwShowFileExt</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;isShowFileExt</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">:&nbsp;</span><span style="color: #000000;">1</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;(ERROR_SUCCESS&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;::RegSetValueEx(hKey,&nbsp;TEXT(</span><span style="color: #000000;">"</span><span style="color: #000000;">Hidden</span><span style="color: #000000;">"</span><span style="color: #000000;">),&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;REG_DWORD,&nbsp;(LPBYTE)</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">dwShowHidden,&nbsp;(DWORD)</span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(dwShowHidden))&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">ERROR_SUCCESS&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;::RegSetValueEx(hKey,&nbsp;TEXT(</span><span style="color: #000000;">"</span><span style="color: #000000;">ShowSuperHidden</span><span style="color: #000000;">"</span><span style="color: #000000;">),&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;REG_DWORD,&nbsp;(LPBYTE)</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">dwShowSysProtected,&nbsp;(DWORD)</span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(dwShowSysProtected))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">&nbsp;ERROR_SUCCESS&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;::RegSetValueEx(hKey,&nbsp;TEXT(</span><span style="color: #000000;">"</span><span style="color: #000000;">HideFileExt</span><span style="color: #000000;">"</span><span style="color: #000000;">),&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;REG_DWORD,&nbsp;(LPBYTE)</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">dwShowFileExt,&nbsp;(DWORD)</span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(dwShowFileExt)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bRet&nbsp;</span><span style="color: #000000;">=</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><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;::RegCloseKey(hKey);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bRet&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(bRet)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;::SendMessageTimeout(HWND_BROADCAST,&nbsp;WM_SETTINGCHANGE,&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;(LPARAM)TEXT(</span><span style="color: #000000;">"</span><span style="color: #000000;">ShellState</span><span style="color: #000000;">"</span><span style="color: #000000;">),&nbsp;SMTO_ABORTIFHUNG,&nbsp;</span><span style="color: #000000;">3000</span><span style="color: #000000;">,&nbsp;NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;::SHChangeNotify(SHCNE_ASSOCCHANGED,&nbsp;SHCNF_IDLIST,&nbsp;NULL,&nbsp;NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;bRet;<br>}</span></div>
<br><img src ="http://www.cppblog.com/xmli/aggbug/89152.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-07-03 13:03 <a href="http://www.cppblog.com/xmli/archive/2009/07/03/89152.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vc中bool与BOOL的区别</title><link>http://www.cppblog.com/xmli/archive/2009/07/03/89138.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 03 Jul 2009 01:48:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/07/03/89138.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/89138.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/07/03/89138.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/89138.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/89138.html</trackback:ping><description><![CDATA[<div class="articletxt">
<p>BOOL是微软定义的typedef&nbsp;&nbsp; int&nbsp;&nbsp;
BOOL。与bool不同，它是一个三值逻辑，TRUE/FALSE/ERROR，返回值为&amp;gt;0的整数为TRUE，0为FALSE，-1为
ERROR。Win32&nbsp;&nbsp; API中很多返回值为BOOL的函数都是三值逻辑。比如GetMessage().</p>
<p>bool是标准C 数据类型，可取值true和false。</p>
<p>根据布尔类型的语义，零值为&#8220;假&#8221;（记为FALSE），任何非零值都是&#8220;真&#8221;（记为TRUE）。TRUE的值究竟是什么并没有统一的标准。例如Visual C 将TRUE定义为1，而Visual Basic则将TRUE定义为-1。 <br>
假设布尔变量名字为flag，它与零值比较的标准if语句如下： <br>
if (flag) // 表示flag为真 <br>
if (!flag) // 表示flag为假 <br>
其它的用法都属于不良风格，例如： <br>
if (flag == TRUE) <br>
if (flag == 1 ) <br>
if (flag == FALSE)&nbsp;&nbsp; <br>
if (flag == 0)</p>
</div><img src ="http://www.cppblog.com/xmli/aggbug/89138.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-07-03 09:48 <a href="http://www.cppblog.com/xmli/archive/2009/07/03/89138.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Utilities for STL std::string</title><link>http://www.cppblog.com/xmli/archive/2009/05/15/83055.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 15 May 2009 07:45:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/05/15/83055.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/83055.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/05/15/83055.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/83055.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/83055.html</trackback:ping><description><![CDATA[<p>Lots of programmers have been familiar with
some routines for string object, such as length, substring, find,
charAt, toLowerCase, toUpperCase, trim, equalsIgnoreCase, startsWith,
endsWith, parseInt, toString, split, and so on.</p>
<p>Now, if you are using STL and its string class std::string, how to do something which above routines do?</p>
<p>Of course, std::string supplies some methods to implements some routines above. They are,</p>
<p><code>length()</code>, get the length of the string.<br><code>substr()</code>, get a substring of the string.<br><code>at()</code>/<code><span class="code-keyword">operator</span> []</code>, get the char at specified location in the string.<br><code>find</code>/<code>rfind()</code>, search a string in a forward/backward direction for a substring.<br><code>find_first_of()</code>, find the first character that is any of specified characters.<br><code>find_first_not_of()</code>, find the first character that is not any of specified characters.<br><code>find_last_of()</code>, find the last character that is any of specified characters.<br><code>find_last_not_of()</code>, find the last character that is not any of specified characters.</p>
<p>Please refer document for more <code>std::<span class="code-SDKkeyword">string</span></code>'s methods </p>
<p>Some routines are not implemented as std::string's methods, but we
can find way in algorithm.h to do that. Of course, the existed methods
of std::string are also used to implement them. </p>
<h3>Transform a string to upper/lower case</h3>
<div class="SmallText" id="premain0" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="0" id="preimg0" width="9" height="9"><span preid="0" style="margin-bottom: 0pt;" id="precollapse0"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre0" lang="C++">std::transform(str.begin(), str.end(), str.begin(), tolower);<br>std::transform(str.begin(), str.end(), str.begin(), toupper);</pre>
<p>Please refer document for detail of std::transform function</p>
<h3>Trim spaces beside a string</h3>
<h4>Trim left spaces</h4>
<div class="SmallText" id="premain1" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="1" id="preimg1" width="9" height="9"><span preid="1" style="margin-bottom: 0pt;" id="precollapse1"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre1" lang="C++"><span class="code-SDKkeyword">string</span>::iterator i;<br><span class="code-keyword">for</span> (i = str.begin(); i != str.end(); i++) {<br>    <span class="code-keyword">if</span> (!isspace(*i)) {<br>        <span class="code-keyword">break</span>;<br>    }<br>}<br><span class="code-keyword">if</span> (i == str.end()) {<br>    str.clear();<br>} <span class="code-keyword">else</span> {<br>    str.erase(str.begin(), i);<br>}</pre>
<h4>Trim right spaces</h4>
<div class="SmallText" id="premain2" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="2" id="preimg2" width="9" height="9"><span preid="2" style="margin-bottom: 0pt;" id="precollapse2"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre2" lang="C++"><span class="code-SDKkeyword">string</span>::iterator i;<br><span class="code-keyword">for</span> (i = str.end() - <span class="code-digit">1</span>; ;i--) {<br>    <span class="code-keyword">if</span> (!isspace(*i)) {<br>        str.erase(i + <span class="code-digit">1</span>, str.end());<br>        <span class="code-keyword">break</span>;<br>    }<br>    <span class="code-keyword">if</span> (i == str.begin()) {<br>        str.clear();<br>        <span class="code-keyword">break</span>;<br>    }<br>}</pre>
<h4>Trim two-sided spaces</h4>
<p>Trim left spaces then trim right spaces. Thus two-sided spaces are trimed.</p>
<h3>Create string by repeating character or substring</h3>
<p>If you want create a string by repeating substring, you must use loop to implement it.</p>
<div class="SmallText" id="premain3" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="3" id="preimg3" width="9" height="9"><span preid="3" style="margin-bottom: 0pt;" id="precollapse3"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre3" lang="C++"><span class="code-SDKkeyword">string</span> repeat(<span class="code-keyword">const</span> string&amp; str, <span class="code-keyword">int</span> n) {<br>    <span class="code-SDKkeyword">string</span> s;<br>    <span class="code-keyword">for</span> (<span class="code-keyword">int</span> i = <span class="code-digit">0</span>; i <span class="code-keyword">&lt;</span> n; i++) {<br>        s += str;<br>    }<br>    <span class="code-keyword">return</span> s;<br>}</pre>
<p>But if you need just to repeat character, std::string has a constructor.</p>
<div class="SmallText" id="premain4" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="4" id="preimg4" width="9" height="9"><span preid="4" style="margin-bottom: 0pt;" id="precollapse4"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre4" lang="C++"><span class="code-SDKkeyword">string</span> repeat(<span class="code-keyword">char</span> c, <span class="code-keyword">int</span> n) {<br>    <span class="code-keyword">return</span> <span class="code-SDKkeyword">string</span>(n, c);<br>}<br></pre>
<h3>Compare ignore case</h3>
<p>It's funny. We should copy the two strings which attend compare.
Then transform all of them to lower case. At last, just compare the two
lower case strings.</p>
<h3>StartsWith and EndsWith</h3>
<h4>StartsWith</h4>
<div class="SmallText" id="premain5" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="5" id="preimg5" width="9" height="9"><span preid="5" style="margin-bottom: 0pt;" id="precollapse5"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre5" lang="C++">str.find(substr) == <span class="code-digit">0</span>;</pre>
<p>If result is <code><span class="code-keyword">true</span></code>, the <code>str</code> starts with <code>substr</code>.</p>
<h4>EndsWith</h4>
<div class="SmallText" id="premain6" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="6" id="preimg6" width="9" height="9"><span preid="6" style="margin-bottom: 0pt;" id="precollapse6"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre6" lang="C++">size_t i = str.rfind(substr);<br><span class="code-keyword">return</span> (i != <span class="code-SDKkeyword">string</span>::npos) &amp;&amp; (i == (str.length() - substr.length()));</pre>
<p>If result is <code><span class="code-keyword">true</span></code>, the <code>str </code>ends with <code>substr</code></p>
<p>There is another way to do that. Just get left substring or right
substring to compare. Because I don't want to calculate if string's
length is enough, so I use find and rfind to do that.</p>
<h3>Parse number/bool from a string</h3>
<p>For these routines, <code>atoi</code>, <code>atol</code> and some other C functions are OK. But I want use C++ way to do. So I choose <code>std::istringstream</code>. the class is in sstream.h.</p>
<p>A template function can do most excludes bool value.</p>
<div class="SmallText" id="premain7" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="7" id="preimg7" width="9" height="9"><span preid="7" style="margin-bottom: 0pt;" id="precollapse7"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre7" lang="C++"><span class="code-keyword">template</span><span class="code-keyword">&lt;</span><span class="code-keyword">class</span> T<span class="code-keyword">&gt;</span> parseString(<span class="code-keyword">const</span> std::string&amp; str) {<br>    T <span class="code-keyword">value</span>;<br>    std::istringstream iss(str);<br>    iss <span class="code-keyword">&gt;</span><span class="code-keyword">&gt;</span> <span class="code-keyword">value</span>;<br>    <span class="code-keyword">return</span> <span class="code-keyword">value</span>;<br>}<br></pre>
<p>The template function can parse 0 as <code><span class="code-keyword">false</span></code> and other number as <code><span class="code-keyword">true</span></code>. But it cannot parse <code><span class="code-string">"</span><span class="code-string">false"</span></code> as <code><span class="code-keyword">false</span></code> and <code><span class="code-string">"</span><span class="code-string">true"</span></code> as <code><span class="code-keyword">true</span></code>. So I write a special function.</p>
<div class="SmallText" id="premain8" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="8" id="preimg8" width="9" height="9"><span preid="8" style="margin-bottom: 0pt;" id="precollapse8"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre8" lang="C++"><span class="code-keyword">template</span><span class="code-keyword">&lt;</span><span class="code-keyword">bool</span><span class="code-keyword">&gt;</span><bool><br><span class="code-keyword">bool</span> parseString(<span class="code-keyword">const</span> std::string&amp; str) {<br>    <span class="code-keyword">bool</span> <span class="code-keyword">value</span>;<br>    std::istringstream iss(str);<br>    iss <span class="code-keyword">&gt;</span><span class="code-keyword">&gt;</span> <strong>boolalpha</strong> <span class="code-keyword">&gt;</span><span class="code-keyword">&gt;</span> <span class="code-keyword">value</span>;<br>    <span class="code-keyword">return</span> <span class="code-keyword">value</span>;<br>}<br></bool></pre>
<p>As you saw, I pass a <code>std::boolalpha</code> flag to the input stream, then the input stream can recognize literal bool value.</p>
<p>It is possible to use a similar way to parse hex string. This time I should pass a <code>std::hex</code> flag to the stream.</p>
<div class="SmallText" id="premain9" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="9" id="preimg9" width="9" height="9"><span preid="9" style="margin-bottom: 0pt;" id="precollapse9"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre9" lang="C++"><span class="code-keyword">template</span><span class="code-keyword">&lt;</span><span class="code-keyword">class</span> T<span class="code-keyword">&gt;</span> parseHexString(<span class="code-keyword">const</span> std::string&amp; str) {<br>    T <span class="code-keyword">value</span>;<br>    std::istringstream iss(str);<br>    iss <span class="code-keyword">&gt;</span><span class="code-keyword">&gt;</span> <strong>hex</strong> <span class="code-keyword">&gt;</span><span class="code-keyword">&gt;</span> <span class="code-keyword">value</span>;<br>    <span class="code-keyword">return</span> <span class="code-keyword">value</span>;<br>}<br></pre>
<h3>To string routines</h3>
<p>Like parsing from string, I will use <code>std::ostringstream</code> to get string from other kinds of value. The class is also in sstream.h. The relative 3 functions are followed.</p>
<div class="SmallText" id="premain10" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="10" id="preimg10" width="9" height="9"><span preid="10" style="margin-bottom: 0pt;" id="precollapse10"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre10" lang="C++"><span class="code-keyword">template</span><span class="code-keyword">&lt;</span><span class="code-keyword">class</span> T<span class="code-keyword">&gt;</span> std::<span class="code-SDKkeyword">string</span> toString(<span class="code-keyword">const</span> T&amp; <span class="code-keyword">value</span>) {<br>    std::ostringstream oss;<br>    oss <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <span class="code-keyword">value</span>;<br>    <span class="code-keyword">return</span> oss.str();<br>}<br><span class="code-SDKkeyword">string</span> toString(<span class="code-keyword">const</span> bool&amp; <span class="code-keyword">value</span>) {<br>    ostringstream oss;<br>    oss <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <strong>boolalpha</strong> <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <span class="code-keyword">value</span>;<br>    <span class="code-keyword">return</span> oss.str();<br>}<br><span class="code-keyword">template</span><span class="code-keyword">&lt;</span><span class="code-keyword">class</span> T<span class="code-keyword">&gt;</span> std::<span class="code-SDKkeyword">string</span> toHexString(<span class="code-keyword">const</span> T&amp; <span class="code-keyword">value</span>, <span class="code-keyword">int</span> width) {<br>    std::ostringstream oss;<br>    oss <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <strong>hex</strong>;<br>    <span class="code-keyword">if</span> (width <span class="code-keyword">&gt;</span> <span class="code-digit">0</span>) {<br>        oss <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <strong>setw(</strong>width<strong>)</strong> <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <strong>setfill(</strong><span class="code-string">'</span><span class="code-string">0'</span><strong>)</strong>;<br>    }<br>    oss <span class="code-keyword">&lt;</span><span class="code-keyword">&lt;</span> <span class="code-keyword">value</span>;<br>    <span class="code-keyword">return</span> oss.str();<br>}<br></pre>
<p>Do you take note of <code>setw</code> and <code>setfill</code>? They are still flags which need an argument. <code>std::setw</code> allow the output thing in the stream occupy fixed width. If itself length is not enough, default uses space to fill. <code>std::setfill</code> is used to change the spaceholder. If you want control the alignment, there are <code>std::left</code> and <code>std::right</code> flags.</p>
<p>Oh, I forgot to tell you, setw and setfill need iomanip.h header file. </p>
<h3><city w:st="on">
<place w:st="on">Split</place>
</city> and tokenizer</h3>
<p>I think split function should be implemented with a tokenizer. So I write a tokenizer at first. We can use <code>find_first_of</code> and <code>find_first_not_of</code> methods to get each token. Follows is nextToken method of Tokenizer class.</p>
<div class="SmallText" id="premain11" style="width: 100%; cursor: pointer;"><img  src="http://www.codeproject.com/images/minus.gif" preid="11" id="preimg11" width="9" height="9"><span preid="11" style="margin-bottom: 0pt;" id="precollapse11"> Collapse</span></div>
<pre style="margin-top: 0pt;" id="pre11" lang="C++"><span class="code-keyword">bool</span> Tokenizer::nextToken(<span class="code-keyword">const</span> std::string&amp; delimiters) {<br>    <span class="code-comment">//</span><span class="code-comment"> find the start character of the next token.</span><br>    size_t i = m_String.find_first_not_of(delimiters, m_Offset);<br>    <span class="code-keyword">if</span> (i == <span class="code-SDKkeyword">string</span>::npos) {<br>        m_Offset = m_String.length();<br>        <span class="code-keyword">return</span> <span class="code-keyword">false</span>;<br>    }<br><br>    <span class="code-comment">//</span><span class="code-comment"> find the end of the token.</span><br>    size_t j = m_String.find_first_of(delimiters, i);<br>    <span class="code-keyword">if</span> (j == <span class="code-SDKkeyword">string</span>::npos) {<br>        m_Token = m_String.substr(i);<br>        m_Offset = m_String.length();<br>        <span class="code-keyword">return</span> <span class="code-keyword">true</span>;<br>    }<br><br>    <span class="code-comment">//</span><span class="code-comment"> to intercept the token and save current position</span><br>    m_Token = m_String.substr(i, j - i);<br>    m_Offset = j;<br>    <span class="code-keyword">return</span> <span class="code-keyword">true</span>;<br>}<br></pre>
<p>The whole Tokenizer is in the source code archive. You can download
it at above. All other functions are still in the source code files.</p>
<!-- Main Page Contents End -->
<div>
<input name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTAyMTMzODg1Ng9kFgJmD2QWBAILD2QWBgIDDw8WAh4HVmlzaWJsZWdkZAIJDw8WAh8AZ2RkAgwPDxYCHwBnZGQCDA9kFggCBw9kFhACAQ9kFgJmDxYCHgtfIUl0ZW1Db3VudGZkAgMPZBYKZg8PFgIeC05hdmlnYXRlVXJsBTsvc2NyaXB0L0FydGljbGVzLy9LQi9zdGwvU1RMX3N0cmluZ191dGlsLmFzcHg/ZGlzcGxheT1QcmludGRkAgEPDxYCHwIFJi9zY3JpcHQvQXJ0aWNsZXMvUmVwb3J0LmFzcHg/YWlkPTE4MTQzZGQCAg8PFgIfAGhkZAIDDw8WAh8AaGRkAgUPDxYCHwIFMS9zY3JpcHQvY29tbW9uL1RlbGxGcmllbmQuYXNweD9vYnRpZD0yJm9iaWQ9MTgxNDNkZAIFDxYCHgRuYW1lBQxDdXJSYXRfMTgxNDMWAmYPZBYCZg9kFgJmD2QWBAIBD2QWAgIBDw8WAh4EVGV4dAUaMzMgdm90ZXMgZm9yIHRoaXMgYXJ0aWNsZS5kZAIHD2QWAmYPZBYEAgEPDxYEHwQFEFBvcHVsYXJpdHk6IDUuMzQfAgUpL3NjcmlwdC9BcnRpY2xlcy9Ub3BBcnRpY2xlcy5hc3B4P3RhX3NvPTFkZAIFDxYCHwQFHFJhdGluZzogPGI+My41MjwvYj4gb3V0IG9mIDVkAg8PZBYCAgEPDxYCHwIFJi9zY3JpcHQvQXJ0aWNsZXMvUmVwb3J0LmFzcHg/YWlkPTE4MTQzZGQCGQ9kFgoCAQ9kFgQCAQ8WAh4JaW5uZXJodG1sBbYBPHA+VGhpcyBhcnRpY2xlLCBhbG9uZyB3aXRoIGFueSBhc3NvY2lhdGVkIHNvdXJjZSBjb2RlIGFuZCBmaWxlcywgaXMgbGljZW5zZWQgdW5kZXIgPGEgaHJlZj0iaHR0cDovL3d3dy5jb2RlcHJvamVjdC5jb20vaW5mby9jcG9sMTAuYXNweCI+VGhlIENvZGUgUHJvamVjdCBPcGVuIExpY2Vuc2UgKENQT0wpPC9hPjwvcD5kAgIPZBYCAgEPEGRkFgBkAgUPFgIfAQIBZAIHDxYCHwQFvgo8aDI+T3RoZXIgcG9wdWxhciBTVEwgYXJ0aWNsZXM6PC9oMj48dWw+PGxpPjxhIGhyZWY9Ii9LQi9zdGwvdmVjdG9yX3ZzX2RlcXVlLmFzcHgiPkFuIEluLURlcHRoIFN0dWR5IG9mIHRoZSBTVEwgRGVxdWUgQ29udGFpbmVyPC9hPjxkaXYgY2xhc3M9IlNtYWxsVGV4dCI+VGhpcyBhcnRpY2xlIHByZXNlbnRzIGFuIGluLWRlcHRoIGFuYWx5c2lzIG9mIHN0ZDo6ZGVxdWUgYW5kIG9mZmVycyBndWlkYW5jZSBhcyB0byB3aGVuIHRvIHByZWZlciB1c2luZyBpdCBhcyBvcHBvc2VkIHRvIHN0ZDo6dmVjdG9yLCBieSB0YWtpbmcgaW50byBjb25zaWRlcmF0aW9uIG1lbW9yeSBhbGxvY2F0aW9uIGFuZCBjb250YWluZXIgcGVyZm9ybWFuY2UuPC9kaXY+PC9saT48bGk+PGEgaHJlZj0iL0tCL3N0bC9ib29zdHNtYXJ0cHRyLmFzcHgiPlNtYXJ0IFBvaW50ZXJzIHRvIGJvb3N0IHlvdXIgY29kZTwvYT48ZGl2IGNsYXNzPSJTbWFsbFRleHQiPkEgYmVnaW5uZXIncyBpbnRyb2R1Y3Rpb24gdG8gdGhlIHNtYXJ0IHBvaW50ZXJzIHByb3ZpZGVkIGJ5IHRoZSBib29zdCBsaWJyYXJ5LjwvZGl2PjwvbGk+PGxpPjxhIGhyZWY9Ii9LQi9zdGwvYmltYXAuYXNweCI+QW4gU1RMLWxpa2UgYmlkaXJlY3Rpb25hbCBtYXA8L2E+PGRpdiBjbGFzcz0iU21hbGxUZXh0Ij5BIHRlbXBsYXRlIGNvbnRhaW5lciBpbXBsZW1lbnRpbmcgYSBiaWRpcmVjdGlvbmFsIG1hcCB0aGF0IGJsZW5kcyB3ZWxsIHdpdGggU1RMLjwvZGl2PjwvbGk+PGxpPjxhIGhyZWY9Ii9LQi9zdGwvdXBncmFkaW5nc3RsYXBwc3RvdW5pY29kZS5hc3B4Ij5VcGdyYWRpbmcgYW4gU1RMLWJhc2VkIGFwcGxpY2F0aW9uIHRvIHVzZSBVbmljb2RlLjwvYT48ZGl2IGNsYXNzPSJTbWFsbFRleHQiPlByb2JsZW1zIHRoYXQgZGV2ZWxvcGVycyB3aWxsIGZhY2Ugd2hlbiB1cGdyYWRpbmcgYW4gU1RMLWJhc2VkIGFwcGxpY2F0aW9uIHRvIHVzZSBVbmljb2RlIGFuZCBob3cgdG8gc29sdmUgdGhlbS48L2Rpdj48L2xpPjxsaT48YSBocmVmPSIvS0Ivc3RsL1NlYXJjaE4uYXNweCI+Q2FuIHNlYXJjaF9uIGJlIG1vcmUgZWZmaWNpZW50PzwvYT48ZGl2IGNsYXNzPSJTbWFsbFRleHQiPlRoaXMgYXJ0aWNsZSBpcyBkaXNjdXNzaW5nIHRoZSBlZmZpY2llbmN5IG9mIHRoZSBtb3N0IHBvcHVsYXIgc2VhcmNoX24gaW1wbGVtZW50YXRpb25zLiBGdXJ0aGVybW9yZSwgaXQgaXMgaW50cm9kdWNpbmcgYSBuZXcgc2VhcmNoX24gc3BlY2lhbGl6YXRpb24gZm9yIHJhbmRvbSBhY2Nlc3MgaXRlcmF0b3JzLCB3aGljaCBvdXRydW5zIGJ5IGZhciB0aGUgbW9zdCBjb21tb25seSB1c2VkIGltcGxlbWVudGF0aW9ucy48L2Rpdj48L2xpPjwvdWw+ZAIJDw8WAh8AZ2RkAg0PFgIeBWNsYXNzBRVBcnRpY2xlVW5lZGl0ZWRIZWFkZXIWAmYPZBYCAgEPZBYCZg9kFgJmDxYCHwMFDlJhdGVJdGVtXzE4MTQzFgICCQ8WAh8AaBYCAgEPEGRkFgBkAhsPDxYCHwBnZGQCHQ8PFgIfAGdkZAIlDxYCHwBoZAILDw8WAh8CBScvc2NyaXB0L0FydGljbGVzL0FydGljbGUuYXNweD9haWQ9MTgxNDNkZAIRDxYCHwQFCzE4IEp1biAyMDA4ZAIVDxYCHwQFHENvcHlyaWdodCAyMDA3IGJ5IGphbWVzZmFuY3lkZFhGezSl4M5gMW/lpwdMf2ltzrTc" type="hidden">
</div>
<h2>License</h2>
<div id="ctl00_LicenseTerms">
<p>This article, along with any associated source code and files, is licensed under <a  href="http://www.codeproject.com/info/cpol10.aspx">The Code Project Open License (CPOL)</a></p>
</div><img src ="http://www.cppblog.com/xmli/aggbug/83055.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-05-15 15:45 <a href="http://www.cppblog.com/xmli/archive/2009/05/15/83055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高手戏玩c++</title><link>http://www.cppblog.com/xmli/archive/2009/05/15/83053.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Fri, 15 May 2009 07:42:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/05/15/83053.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/83053.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/05/15/83053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/83053.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/83053.html</trackback:ping><description><![CDATA[<pre class="csharp" name="code">toupper,tolower<br>地球人都知道 C++ 的 string 没有 toupper ，好在这不是个大问题，因为我们有 STL 算法：<br><br>string s("heLLo");<br>transform(s.begin(), s.end(), s.begin(), ::toupper);<br>cout &lt;&lt; s &lt;&lt; endl;<br>transform(s.begin(), s.end(), s.begin(), ::tolower);<br>cout &lt;&lt; s &lt;&lt; endl;<br><br>当然，我知道很多人希望的是 s.to_upper() ，但是对于一个这么通用的 basic_string 来说，的确没办法把这些专有的方法放进来。如果你用 boost stringalgo ，那当然不在话下，你也就不需要读这篇文章了。<br><br>------------------------------------------------------------------------<br>trim<br>我们还知道 string 没有 trim ，不过自力更生也不困难，比 toupper 来的还要简单：<br><br>    string s("   hello   ");<br>    s.erase(0, s.find_first_not_of(" \n"));<br>    cout &lt;&lt; s &lt;&lt; endl;<br>    s.erase(s.find_last_not_of(' ') + 1);<br>    cout &lt;&lt; s &lt;&lt; endl;<br><br>注意由于 find_first_not_of 和 find_last_not_of 都可以接受字符串，这个时候它们寻找该字符串中所有字符的 absence ，所以你可以一次 trim 掉多种字符。<br><br>-----------------------------------------------------------------------<br>erase<br>string 本身的 erase 还是不错的，但是只能 erase 连续字符，如果要拿掉一个字符串里面所有的某个字符呢？用 STL 的 erase + remove_if 就可以了，注意光 remove_if 是不行的。<br><br>    string s("   hello, world. say bye   ");<br>    s.erase(remove_if(s.begin(),s.end(),<br>        bind2nd(equal_to<char></char>(), ' ')),<br>    s.end());<br><br>上面的这段会拿掉所有的空格，于是得到 hello,world.saybye。<br><br>-----------------------------------------------------------------------<br>replace<br>string 本身提供了 replace ，不过并不是面向字符串的，譬如我们最常用的把一个 substr 换成另一个 substr 的操作，就要做一点小组合：<br><br>    string s("hello, world");<br>    string sub("ello, ");<br>    s.replace(s.find(sub), sub.size(), "appy ");<br>    cout &lt;&lt; s &lt;&lt; endl;<br><br>输出为 happy world。注意原来的那个 substr 和替换的 substr 并不一定要一样长。<br><br>-----------------------------------------------------------------------<br>startwith, endwith<br>这两个可真常用，不过如果你仔细看看 string 的接口，就会发现其实没必要专门提供这两个方法，已经有的接口可以干得很好：<br><br>    string s("hello, world");<br>    string head("hello");<br>    string tail("ld");<br>    bool startwith = s.compare(0, head.size(), head) == 0;<br>    cout &lt;&lt; boolalpha &lt;&lt; startwith &lt;&lt; endl;<br>    bool endwith = s.compare(s.size() - tail.size(), tail.size(), tail) == 0;<br>    cout &lt;&lt; boolalpha &lt;&lt; endwith &lt;&lt; endl;<br><br>当然了，没有 s.startwith("hello") 这样方便。<br><br>------------------------------------------------------------------------<br>toint, todouble, tobool...<br>这也是老生常谈了，无论是 C 的方法还是 C++ 的方法都可以，各有特色：<br><br>    string s("123");<br>    int i = atoi(s.c_str());<br>    cout &lt;&lt; i &lt;&lt; endl;<br>   <br>    int ii;<br>    stringstream(s) &gt;&gt; ii;<br>    cout &lt;&lt; ii &lt;&lt; endl;<br>   <br>    string sd("12.3");<br>    double d = atof(sd.c_str());<br>    cout &lt;&lt; d &lt;&lt; endl;<br>   <br>    double dd;<br>    stringstream(sd) &gt;&gt; dd;<br>    cout &lt;&lt; dd &lt;&lt; endl;<br>   <br>    string sb("true");<br>    bool b;<br>    stringstream(sb) &gt;&gt; boolalpha &gt;&gt; b;<br>    cout &lt;&lt; boolalpha &lt;&lt; b &lt;&lt; endl;<br><br>C 的方法很简洁，而且赋值与转换在一句里面完成，而 C++ 的方法很通用。<br><br>------------------------------------------------------------------------<br>split<br>这可是件麻烦事，我们最希望的是这样一个接口： s.split(vect, ',') 。用 STL 算法来做有一定难度，我们可以从简单的开始，如果分隔符是空格、tab 和回车之类，那么这样就够了：<br><br>    string s("hello world, bye.");<br>    vector<string></string> vect;<br>    vect.assign(<br>        istream_iterator<string></string>(stringstream(s)),<br>        istream_iterator<string></string>()<br>    );<br><br>不过要注意，如果 s 很大，那么会有效率上的隐忧，因为 stringstream 会 copy 一份 string 给自己用。<br><br>------------------------------------------------------------------------<br>concat<br>把一个装有 string 的容器里面所有的 string 连接起来，怎么做？希望你不要说是 hand code 循环，这样做不是更好？<br><br>    vector<string></string> vect;<br>    vect.push_back("hello");<br>    vect.push_back(", ");<br>    vect.push_back("world");<br>   <br>    cout &lt;&lt; accumulate(vect.begin(), vect.end(), string(""));<br><br>不过在效率上比较有优化余地。<br><br>-------------------------------------------------------------------------<br><br>reverse<br>其实我比较怀疑有什么人需要真的去 reverse 一个 string ，不过做这件事情的确是很容易：<br><br>  std::reverse(s.begin(), s.end());<br><br>上面是原地反转的方法，如果需要反转到别的 string 里面，一样简单：<br><br>  s1.assign(s.rbegin(), s.rend());<br><br>效率也相当理想。<br><br>-------------------------------------------------------------------------<br><br>解析文件扩展名<br>字数多点的写法：<br><br>    std::string filename("hello.exe");<br><br>    std::string::size_type pos = filename.rfind('.');<br>    std::string ext = filename.substr(pos == std::string::npos ? filename.length() : pos + 1);<br><br>不过两行，合并成一行呢？也不是不可以：<br><br>    std::string ext = filename.substr(filename.rfind('.') == std::string::npos ? filename.length() : filename.rfind('.') + 1);<br><br>我知道，rfind 执行了两次。不过第一，你可以希望编译器把它优化掉，其次，扩展名一般都很短，即便多执行一次，区别应该是相当微小。<br>STL 算法<br>distance<br>很多时候我们希望在一个 vector ，或者 list ，或者什么其他东西里面，找到一个值在哪个位置，这个时候 find 帮不上忙，而有人就转而求助手写循环了，而且是原始的手写循环：<br><br>for ( int i = 0; i &lt; vect.size(); ++i)<br>    if ( vect[i] == value ) break;<br><br>如果编译器把 i 看作 for scope 的一部分，你还要把 i 的声明拿出去。真的需要这样么？看看这个：<br><br>    int dist =<br>        distance(col.begin(),<br>            find(col.begin(), col.end(), 5));<br><br>其中 col 可以是很多容器，list, vector, deque... 当然这是你确定 5 就在 col 里面的情形，如果你不确定，那就加点判断：<br><br>    int dist;<br>    list<int></int>::iterator pos = find(col.begin(), col.end(), 5);<br>    if ( pos != col.end() )<br>        dist = distance(col.begin(), pos);<br><br>我想这还是比手写循环来的好些吧。<br><br>--------------------------------------------------------------------------<br>max, min<br>这是有直接的算法支持的，当然复杂度是 O(n)，用于未排序容器，如果是排序容器...老兄，那还需要什么算法么？<br><br>max_element(col.begin(), col.end());<br>min_element(col.begin(), col.end());<br><br>注意返回的是 iterator ，如果你关心的只是值，那么好：<br><br>*max_element(col.begin(), col.end());<br>*min_element(col.begin(), col.end());<br><br>max_element 和 min_element 都默认用 less 来排序，它们也都接受一个 binary predicate ，如果你足够无聊，甚至可以把 max_element 当成 min_element 来用，或者反之：<br><br>*max_element(col.begin(), col.end(), greater<int></int>()); // 返回最小值！<br>*min_element(col.begin(), col.end(), greater<int></int>()); // 返回最大值<br><br>当然它们的本意不是这个，而是让你能在比较特殊的情况下使用它们，例如，你要比较的是每个元素的某个成员，或者成员函数的返回值。例如：<br><br>#include <iostream></iostream><br>#include
<list></list>
<br>#include <algorithm></algorithm><br>#include <string></string><br>#include <boost bind.hpp=""></boost><br><br>using namespace boost;<br>using namespace std;<br><br>struct Person<br>{<br>    Person(const string&amp; _name, int _age)<br>        : name(_name), age(_age)<br>    {}<br>    int age;<br>    string name;<br>};<br><br>int main()<br>{<br>    list
<person></person>
col;<br>    list
<person></person>
::iterator pos;<br><br>    col.push_back(Person("Tom", 10));<br>    col.push_back(Person("Jerry", 12));<br>    col.push_back(Person("Mickey", 9));<br><br>    Person eldest =<br>        *max_element(col.begin(), col.end(),<br>            bind(&amp;Person::age, _1) &lt; bind(&amp;Person::age, _2));//&gt;=1.33<br>   <br>    cout &lt;&lt; eldest.name;<br>}<br><br>输出是 Jerry ，这里用了 boost.bind ，原谅我不知道用 bind2nd, mem_fun 怎么写，我也不想知道...<br><br>-------------------------------------------------------------------------<br>copy_if<br>没错，STL 里面压根没有 copy_if ，这就是为什么我们需要这个：<br><br>template<typename predicate="" typename=""></typename><br>OutputIterator copy_if(<br>    InputIterator begin, InputIterator end, OutputIterator destBegin, Predicate p)<br>{<br>    while (begin != end)<br>    {<br>        if (p(*begin))*destBegin++ = *begin;<br>        ++begin;<br>    }<br>    return destBegin;<br>}<br><br>把它放在自己的工具箱里，是一个明智的选择。<br><br>------------------------------------------------------------------------<br>惯用手法：erase(iter++)<br>如果你要去除一个 list 中的某些元素，那可千万小心：（下面的代码是错的！！！）<br><br>#include <iostream></iostream><br>#include <algorithm></algorithm><br>#include <iterator></iterator><br>#include
<list></list>
<br><br>int main()<br>{<br>    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};<br>    std::list<int></int> lst(arr, arr + 10);<br><br>    for ( std::list<int></int>::iterator iter = lst.begin();<br>          iter != lst.end(); ++iter)<br>        if ( *iter % 2 == 0 )<br>            lst.erase(iter);<br>           <br>    std::copy(lst.begin(), lst.end(),<br>        std::ostream_iterator<int></int>(std::cout, " "));<br>}<br><br>当 iter 被 erase 掉的时候，它已经失效，而后面却还会做 ++iter ，其行为无可预期！如果你不想动用 remove_if ，那么唯一的选择就是：<br><br>#include <iostream></iostream><br>#include <algorithm></algorithm><br>#include <iterator></iterator><br>#include
<list></list>
<br><br>int main()<br>{<br>    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};<br>    std::list<int></int> lst(arr, arr + 10);<br><br>    for ( std::list<int></int>::iterator iter = lst.begin();<br>          iter != lst.end(); )<br>        if ( *iter % 2 == 0 )<br>            lst.erase(iter++);<br>        else<br>            ++iter;<br>          <br>    std::copy(lst.begin(), lst.end(),<br>        std::ostream_iterator<int></int>(std::cout, " "));<br>}<br><br>但是上面的代码不能用于 vector, string 和 deque ，因为对于这些容器， erase 不光令 iter 失效，还令 iter 之后的所有 iterator 失效！<br><br>-------------------------------------------------------------------------<br>erase(remove...) 惯用手法<br>上面的循环如此难写，如此不通用，如此不容易理解，还是用 STL 算法来的好，但是注意，光 remove_if 是没用的，必须使用 erase(remove...) 惯用手法：<br><br>#include <iostream></iostream><br>#include <algorithm></algorithm><br>#include <iterator></iterator><br>#include
<list></list>
<br>#include <functional></functional><br>#include <boost bind.hpp=""></boost><br><br>int main()<br>{<br>    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};<br>    std::list<int></int> lst(arr, arr + 10);<br><br>    lst.erase(remove_if(lst.begin(), lst.end(),<br>        boost::bind(std::modulus<int></int>(), _1, 2) == 0),<br>        lst.end()<br>    );<br>          <br>    std::copy(lst.begin(), lst.end(),<br>        std::ostream_iterator<int></int>(std::cout, " "));<br>}<br><br>当然，这里借助了 boost.bind ，让我们不用多写一个没用的 functor 。<br><br>简单常识——关于stream<br>从文件中读入一行<br><br>简单，这样就行了：<br><br>ifstream ifs("input.txt");<br>char buf[1000];<br><br>ifs.getline(buf, sizeof buf);<br><br>string input(buf);<br><br>当然，这样没有错，但是包含不必要的繁琐和拷贝，况且，如果一行超过1000个字符，就必须用一个循环和更麻烦的缓冲管理。下面这样岂不是更简单？<br><br>string input;<br>input.reserve(1000);<br>ifstream ifs("input.txt");<br>getline(ifs, input);<br><br>不仅简单，而且安全，因为全局函数 getline 会帮你处理缓冲区用完之类的麻烦，如果你不希望空间分配发生的太频繁，只需要多 reserve 一点空间。<br><br>这就是&#8220;简单常识&#8221;的含义，很多东西已经在那里，只是我一直没去用。<br><br>---------------------------------------------------------------------------<br><br>一次把整个文件读入一个 string<br><br>我希望你的答案不要是这样：<br><br>string input;<br>while( !ifs.eof() )<br>{<br>    string line;<br>    getline(ifs, line);<br>    input.append(line).append(1, '\n');<br>}<br><br>当然了，没有错，它能工作，但是下面的办法是不是更加符合 C++ 的精神呢？<br><br>string input(<br>    istreambuf_iterator<char></char>(instream.rdbuf()),<br>    istreambuf_iterator<char></char>()<br>);<br><br>同样，事先分配空间对于性能可能有潜在的好处：<br><br>string input;<br>input.reserve(10000);<br>input.assign(<br>    istreambuf_iterator<char></char>(ifs.rdbuf()),<br>    istreambuf_iterator<char></char>()<br>);<br><br>很简单，不是么？但是这些却是我们经常忽略的事实。<br>补充一下，这样干是有问题的：<br><br>    string input;<br>    input.assign(<br>        istream_iterator<char></char>(ifs),<br>        istream_iterator<char></char>()<br>    );<br><br>因为它会忽略所有的分隔符，你会得到一个纯&#8220;字符&#8221;的字符串。最后，如果你只是想把一个文件的内容读到另一个流，那没有比这更快的了：<br><br>    fstream fs("temp.txt");<br>    cout &lt;&lt; fs.rdbuf();<br><br>因此，如果你要手工 copy 文件，这是最好的（如果不用操作系统的 API）：<br><br>   ifstream ifs("in.txt");<br>   ofstream ofs("out.txt");<br>   ofs &lt;&lt; in.rdbuf();<br><br>-------------------------------------------------------------------------<br><br>open 一个文件的那些选项<br><br>ios::in     Open file for reading<br>ios::out    Open file for writing<br>ios::ate    Initial position: end of file<br>ios::app    Every output is appended at the end of file<br>ios::trunc  If the file already existed it is erased<br>ios::binary Binary mode<br><br>-------------------------------------------------------------------------<br><br>还有 ios 的那些 flag<br><br>    flag 	effect if set<br>    ios_base::boolalpha 	input/output bool objects as alphabetic names (true, false).<br>    ios_base::dec 	input/output integer in decimal base format.<br>    ios_base::fixed 	output floating point values in fixed-point notation.<br>    ios_base::hex 	input/output integer in hexadecimal base format.<br>    ios_base::internal 	the output is filled at an internal point enlarging the output up to the field width.<br>    ios_base::left 	the output is filled at the end enlarging the output up to the field width.<br>    ios_base::oct 	input/output integer in octal base format.<br>    ios_base::right 	the output is filled at the beginning enlarging the output up to the field width.<br>    ios_base::scientific 	output floating-point values in scientific notation.<br>    ios_base::showbase 	output integer values preceded by the numeric base.<br>    ios_base::showpoint 	output floating-point values including always the decimal point.<br>    ios_base::showpos 	output non-negative numeric preceded by a plus sign (+).<br>    ios_base::skipws 	skip leading whitespaces on certain input operations.<br>    ios_base::unitbuf 	flush output after each inserting operation.<br>    ios_base::uppercase 	output uppercase letters replacing certain lowercase letters.<br><br>There are also defined three other constants that can be used as masks:<br><br>    constant 	value<br>    ios_base::adjustfield 	left | right | internal<br>    ios_base::basefield 	dec | oct | hex<br>    ios_base::floatfield 	scientific | fixed<br><br>--------------------------------------------------------------------------<br><br>用我想要的分隔符来解析一个字符串，以及从流中读取数据<br><br>这曾经是一个需要不少麻烦的话题，由于其常用而显得尤其麻烦，但是其实 getline 可以做得不错：<br><br>    getline(cin, s, ';');   <br>    while ( s != "quit" )<br>    {<br>        cout &lt;&lt; s &lt;&lt; endl;<br>        getline(cin, s, ';');<br>    }<br><br>简单吧？不过注意，由于这个时候 getline 只把 ; 作为分隔符，所以你需要用 ;quit; 来结束输入，否则 getline 会把前后的空格和回车都读入 s ，当然，这个问题可以在代码里面解决。<br><br>同样，对于简单的字符串解析，我们是不大需要动用什么 Tokenizer 之类的东西了：<br><br>#include <iostream></iostream><br>#include <sstream></sstream><br>#include <string></string><br><br>using namespace std;<br><br>int main()<br>{<br>    string s("hello,world, this is a sentence; and a word, end.");<br>    stringstream ss(s);<br>   <br>    for ( ; ; )<br>    {<br>        string token;<br>        getline(ss, token, ',');<br>        if ( ss.fail() ) break;<br>       <br>        cout &lt;&lt; token &lt;&lt; endl;<br>    }<br>}<br><br>输出：<br><br>hello<br>world<br> this is a sentence; and a word<br> end.<br><br>很漂亮不是么？不过这么干的缺陷在于，只有一个字符可以作为分隔符。<br><br>--------------------------------------------------------------------------<br><br>把原本输出到屏幕的东西输出到文件，不用到处去把 cout 改成 fs<br>#include <iostream></iostream><br>#include <fstream></fstream><br>using namespace std;<br>int main()<br>{    <br>    ofstream outf("out.txt"); <br>    streambuf *strm_buf=cout.rdbuf();    <br>    cout.rdbuf(outf.rdbuf()); <br>    cout&lt;&lt;"write something to file"&lt;<br>#include <fstream></fstream><br>#include <sstream></sstream><br>#include <algorithm></algorithm><br>#include <vector></vector><br>#include <iterator></iterator><br><br>using namespace std;<br><br>int main()<br>{  <br>    vector<int></int> vect;<br>    for ( int i = 1; i &lt;= 9; ++i )<br>        vect.push_back(i);<br>       <br>    copy(vect.begin(), vect.end(),<br>        ostream_iterator<int></int>(cout, " ")<br>    );<br>    cout &lt;&lt; endl;<br>   <br>    ostream_iterator<double></double> os_iter(cout, " ~ ");<br>    *os_iter = 1.0;<br>    os_iter++;<br>    *os_iter = 2.0;<br>    *os_iter = 3.0;<br>}<br><br>输出：<br><br>1 2 3 4 5 6 7 8 9<br>1 ~ 2 ~ 3 ~<br><br>很明显，ostream_iterator 的作用就是允许对 stream 做 iterator 的操作，从而让算法可以施加于 stream 之上，这也是 STL 的精华。与前面的&#8220;读取文件&#8221;相结合，我们得到了显示一个文件最方便的办法：<br><br>    copy(istreambuf_iterator<char></char>(ifs.rdbuf()),<br>         istreambuf_iterator<char></char>(),<br>         ostreambuf_iterator<char></char>(cout)<br>    );<br><br>同样，如果你用下面的语句，得到的会是没有分隔符的输出：<br><br>    copy(istream_iterator<char></char>(ifs),<br>         istream_iterator<char></char>(),<br>         ostream_iterator<char></char>(cout)<br>    );<br><br>那多半不是你要的结果。如果你硬是想用 istream_iterator 而不是 istreambuf_iterator 呢？还是有办法：<br><br>    copy(istream_iterator<char></char>(ifs &gt;&gt; noskipws),<br>         istream_iterator<char></char>(),<br>         ostream_iterator<char></char>(cout)<br>    );<br><br>但是这样不是推荐方法，它的效率比第一种低不少。<br>如果一个文件 temp.txt 的内容是下面这样，那么我的这个从文件中把数据读入 vector 的方法应该会让你印象深刻。<br><br>12345 234 567<br>89    10<br><br>程序：<br><br>#include <iostream></iostream><br>#include <fstream></fstream><br>#include <algorithm></algorithm><br>#include <vector></vector><br>#include <iterator></iterator><br><br>using namespace std;<br><br>int main()<br>{  <br>    ifstream ifs("temp.txt");<br>   <br>    vector<int></int> vect;<br>    vect.assign(istream_iterator<int></int>(ifs),<br>        istream_iterator<int></int>()<br>    );<br><br>    copy(vect.begin(), vect.end(), ostream_iterator<int></int>(cout, " "));<br>}<br><br>输出：<br><br>12345 234 567 89 10<br><br>很酷不是么？判断文件结束、移动文件指针之类的苦工都有 istream_iterator 代劳了。<br><br>-----------------------------------------------------------------------<br><br>其它算法配合 iterator<br><br>计算文件行数：<br><br>    int line_count =<br>        count(istreambuf_iterator<char></char>(ifs.rdbuf()),<br>              istreambuf_iterator<char></char>(),<br>              '\n');       <br><br>当然确切地说，这是在计算文件中回车符的数量，同理，你也可以计算文件中任何字符的数量，或者某个 token 的数量：<br><br>    int token_count =<br>        count(istream_iterator<string></string>(ifs),<br>              istream_iterator<string></string>(),<br>              "#include");       <br><br>注意上面计算的是 &#8220;#include&#8221; 作为一个 token 的数量，如果它和其他的字符连起来，是不算数的。<br><br>------------------------------------------------------------------------<br>Manipulator<br><br>Manipulator 是什么？简单的说，就是一个接受一个 stream 作为参数，并且返回一个 stream 的函数，比如上面的 unskipws ，它的定义是这样的：<br><br>  inline ios_base&amp;<br>  noskipws(ios_base&amp; __base)<br>  {<br>    __base.unsetf(ios_base::skipws);<br>    return __base;<br>  }<br><br>这里它用了更通用的 ios_base 。知道了这一点，你大概不会对自己写一个 manipulator 有什么恐惧感了，下面这个无聊的 manipulator 会忽略 stream 遇到第一个分号之前所有的输入（包括那个分号）：<br><br>template <class class="" traits=""></class><br>inline std::basic_istream&amp;<br>ignoreToSemicolon (std::basic_istream&amp; s)<br>{<br>    s.ignore(std::numeric_limits<int></int>::max(), s.widen(';'));<br>    return s;<br>}<br><br>不过注意，它不会忽略以后的分号，因为 ignore 只执行了一次。更通用一点，manipulator 也可以接受参数的，下面这个就是 ignoreToSemicolon 的通用版本，它接受一个参数， stream 会忽略遇到第一个该参数之前的所有输入，写起来稍微麻烦一点：<br><br>struct IgnoreTo {<br>    char ignoreTo;<br>    IgnoreTo(char c) : ignoreTo(c)<br>    {}<br>};<br>   <br>std::istream&amp; operator &gt;&gt; (std::istream&amp; s, const IgnoreTo&amp; manip)<br>{<br>    s.ignore(std::numeric_limits<int></int>::max(), s.widen(manip.ignoreTo));<br>    return s;<br>}<br><br>但是用法差不多：<br><br>    copy(istream_iterator<char></char>(ifs &gt;&gt; noskipws &gt;&gt; IgnoreTo(';')),<br>         istream_iterator<char></char>(),<br>         ostream_iterator<char></char>(cout)<br>    );<br><br>其效果跟 IgnoreToSemicolon 一样。<br></pre><img src ="http://www.cppblog.com/xmli/aggbug/83053.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-05-15 15:42 <a href="http://www.cppblog.com/xmli/archive/2009/05/15/83053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++开源跨平台类库及在VC++.net中应用的配置(转)</title><link>http://www.cppblog.com/xmli/archive/2009/02/25/74860.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 25 Feb 2009 02:06:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/02/25/74860.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/74860.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/02/25/74860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/74860.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/74860.html</trackback:ping><description><![CDATA[在如下的库支持下，开发的系统可以很方便移植到当前大部分平台上运行而无需改动，只需在对应的平台下 用你喜欢的编译器重新编译即可。
<p><strong>经典的C++库</strong>：<br>　　STLport-------SGI STL库的跨平台可移植版本，在以前有些编译器离符合标准比较远的情况下 那时还是有用的，当然目前vc71已经比较接近标准了，故目前不怎么用它了。</p>
<p>　　Boost---------准标准库， 功能强大 涉及能想的到的大部分非特别领域的算法，有一个大的C++社区支持。</p>
<p>　　WxWindows-----功能强大的跨平台GUI库 ，它的功能和结构都类似 MFC，故原则上可以通过WxWindows把现有MFC程序移植到非Win平台下。</p>
<p>　　Blitz---------高效率的数值计算函数库 ,你可以订制补充你需要的算法。</p>
<p>　　Log4cpp-------日志处理 ，功能类似java中的log4j。</p>
<p>　　ACE-----------自适应通讯环境， 重量级的通讯环境库。</p>
<p>　　Crypto++ -----加/解密算法库, 非常专业的C++ 密码学函式库。</p>
<p>　　CppUnit --- 一个c++的单元测试框架 类似 java 的JUnit。</p>
<p>　　Loki ------- 一个实验性质的库，尝试把类似设计模式这样思想层面的东西通过库来提供,他是C++的一个模板库,系C++"贵族"， 它把C++模板的功能发挥到了极致。</p>
<p><strong>学术性的C++库: </strong></p>
<p>　　FC++ --------The Functional C++ Library ,用库来扩充语言的一个代表作 ,模板库。</p>
<p>　　CGAL ------- Computational Geometry Algorithms Library计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。</p>
<p><strong>其它目前我感觉还不是很爽的C++库：</strong> </p>
<p>　　Doxygen ----注释文档生成工具 ,可恨的是我找不到 windows版本。</p>
<p>　　QT ----------大名顶顶的一个多平台的C++图形用户界面应用程序框架（GUI库）可气的是他的 Windows版 是商业发布的要付费。</p>
<p>　　xml4c--------IBM开发的XML Parser，系超重量级的， 适用大型应用中， 其DLL有 12M，恐怖吧。</p>
<p>　　Xerces c++ --Apache的XML项目， 但 只支持少数的字符编码，如ASCII，UTF-8，UTF-16等，不能处理包含中文字符的XML文档。</p>
<p>　　XMLBooster ----- 也是一种 XML的 解析工具。</p>
<p>　　Fox -------又一种开放源代码（C++）的GUI库，功能不是很强。</p>
<p><strong>C++开发环境(Win平台下除了 Visual C++ 和 Borland C++以外的)：</strong></p>
<p>　　Cygwin --------Windows下的一个Unix仿真环境。</p>
<p>　　MinGW --------GCC的一个Windows移植版本。</p>
<p>　　Dev C++ -------- 一个C/C++ 的集成开发环境，在Windows上的C++编译器一直和标准有着一段距离的时候，GCC就是一个让Windows下开发者流口水的编译器。</p>
<p>　　Eclipse-CDT ----IMB 开发的一个集成开发环境，一般用来作为Java 开发环境，但由于Eclipse 是通过插件体系来扩展功能，这里我们 安装 CDT插件后，就可以用来作为C++ 的集成开发环境。</p>
<p><br><br><strong>经典的C++库在VC++.net中应用的配置</strong><br><br>以下以 vc71环境 为例，其他环境 见各软件包的说明文档。</p>
<p>1. STLport (SGI STL库的跨平台可移植版本。) <br>-------<a  href="http://www.stlport.org/" target="_blank">http://www.stlport.org</a></p>
<p>vc71环境中编译安装<br>版本：STLport-4.6.2.tar.gz<br>copy vc71.mak makefile<br>nmake clean all</p>
<p>头文件在 %STLport_root%/include\stlport<br>库文件在 %STLport_root%/lib</p>
<p>头文件添加方法如：<br>#include 需要链接lib库</p>
<p>2 WxWindows (跨平台的GUI库)<br>--------<a  href="http://www.wxwindows.org/" target="_blank">http://www.wxwindows.org</a><br>--------<a  href="http://sourceforge.net/projects/wxwindows" target="_blank">http://sourceforge.net/projects/wxwindows</a><br>--------<a  href="http://i18n.linux.net.cn/others/wxWindowstut/wxTutorial.html" target="_blank">http://i18n.linux.net.cn/others/wxWindowstut/wxTutorial.html</a></p>
<p>　　因为其类层次极像MFC，所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库，支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。</p>
<p>vc71环境中编译安装<br>版本：wxMSW-2.6.0-Setup.exe<br>copy makefile.vc makefile<br>通过 配置 config.vc 的 SHARED = 0 和 BUILD = debug<br>确定 nmake clean all 的四种编译结果：</p>
<p>include头文件： include\wx<br>Lib库文件: lib\vc_dll 和 lib\vc_lib<br>DLL: lib\vc_dll </p>
<p>头文件在 %wxWidgets_root%/include\wx<br>库文件在 %wxWidgets_root%/lib\vc_dll 和 %wxWidgets_root%/lib\vc_lib</p>
<p>头文件添加方法如：<br>#include 需要链接lib库 </p>
<p>3 boost (&#8220;准&#8221;标准库)<br>------<a  href="http://www.boost.org/" target="_blank">http://www.boost.org/</a><br>------<a  href="http://sourceforge.net/projects/boost/" target="_blank">http://sourceforge.net/projects/boost/</a></p>
<p>　　Boost库是一个经过千锤百炼、可移植、提供源代码的C++库，作为标准库的后备，是C++标准化进程的发动机之一。
Boost库由C++标准委员会库工作组成员发起，在C++社区中影响甚大，其成员已近2000人。
Boost库为我们带来了最新、最酷、最实用的技术，是不折不扣的&#8220;准&#8221;标准库。</p>
<p>vc71环境中编译安装<br>版本：boost_1_32_0.exe</p>
<p>首先进入 tools\build\jam_src 运行 build.bat 得到一个工具： bjam.exe<br>将其复制到 boost_root 目录下<br>执行 bjam "-sTOOLS=vc-7_1" stage 开始编译 （bjam "-sTOOLS=vc-7_1" install） </p>
<p>头文件在 %boost_root%/boost<br>库文件在 %boost_root%/stage\lib </p>
<p>头文件添加方法如：<br>#include 有时要链接lib库</p>
<p>　　Boost中比较有名气的有这么几个库：<br>　　Regex正则表达式库<br>　　Spirit<br>　　LL parser framework，用C++代码直接表达EBNF<br>　　Graph图组件和算法<br>　　Lambda在调用的地方定义短小匿名的函数对象，很实用的functional功能<br>　　concept check检查泛型编程中的concept<br>　　Mpl用模板实现的元编程框架<br>　　Thread可移植的C++多线程库<br>　　Python把C++类和函数映射到Python之中<br>　　Pool内存池管理<br>　　smart_ptr5个智能指针，学习智能指针必读，一份不错的参考是来自CUJ的文章：Smart Pointers in Boost,哦，这篇文章可以查到，CUJ是提供在线浏览的。</p>
<p>　　Boost总体来说是实用价值很高，质量很高的库。并且由于其对跨平台的强调，对标准C++的强调，是编写平台无关，现代C++的开发者必备的
工具。但是Boost中也有很多是实验性质的东西，在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展，其构造用尽精巧的手
法，不要贸然的花费时间研读。Boost另外一面，比如Graph这样的库则是具有工业强度，结构良好，非常值得研读的精品代码，并且也可以放心的在产品
代码中多多利用。 </p>
<p>４ blitz (高效率的数值计算函数库) <br>------<a  href="http://folk.uio.no/patricg/blitz/html/index.html" target="_blank">http://folk.uio.no/patricg/blitz/html/index.html</a><br>------<a  href="http://www.oonumerics.org/blitz/" target="_blank">http://www.oonumerics.org/blitz/</a><br>------<a  href="http://sourceforge.net/projects/blitz/" target="_blank">http://sourceforge.net/projects/blitz/</a></p>
<p>　　Blitz++ 是一个高效率的数值计算函数库，它的设计目的是希望建立一套既具像C++
一样方便，同时又比Fortran速度更快的数值计算环境。通常，用C++所写出的数值程序，比
Fortran慢20%左右，因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术，程序执行甚至可以比Fortran更快。</p>
<p>　　Blitz++目前仍在发展中，对于常见的SVD，FFTs，QMRES等常见的线性代数方法并不提供，不过使用者可以很容易地利用Blitz++所提供的函数来构建。</p>
<p>vc71环境中编译安装<br>版本：blitz-0.8.tar.gz</p>
<p>将 blitz-0.8/Blitz-VS.NET.zip 解压到当前目录下<br>打开 Blitz-Library.sln 编译即可</p>
<p>头文件在 %blitz_root%/blitz<br>%blitz_root%/random<br>库文件在 %blitz_root%/lib （静态库）</p>
<p>头文件添加方法如：<br>#include 有时要链接lib库<br>#include 不需要lib库</p>
<p>５ log4cpp (日志处理)<br>-------<a  href="http://sourceforge.net/projects/log4cpp/" target="_blank">http://sourceforge.net/projects/log4cpp/</a><br>-------<a  href="http://log4cpp.hora-obscura.de/index.php/Main_Page" target="_blank">http://log4cpp.hora-obscura.de/index.php/Main_Page</a></p>
<p>　　Log4cpp 是 Log4J 的 C++ 移植版本，开放源代码并且完全免费。与 Log4J
能够跨平台一样，Log4cpp也致力于写出跨平台的 C++ 程序。Log4cpp 主要是用于 C++ 程序中写 log
文件，与此同时，Log4cpp 中有很多有用的类库，对于写跨平台 C++ 程序的人来说，可以直接拿来用，或者作为自己写跨平台类的参考。</p>
<p>　　Log4cpp 中的跨平台类库有明显的 Java 痕迹，比如 Class、Object 、Loader、Locale 等类。 Log4cpp<br>中的类都可以根据类名 new 出一个 instance，其实现的方式和 MFC 如出一辙：通过 C++ 强大的宏来实现。</p>
<p>Log4cpp 中的跨平台类库主要有:</p>
<p>信号类：Condition（broadcast，signal，wait），CriticalSection （lock，unlock），WaitAccess，<br>Event（set，reset，wait），Mutex（lock，unlock）， Semaphore（wait，tryWait，post）</p>
<p>网络类：InetAddress，Socket，ServerSocket，DatagramSocket，SocketInputStream，<br>SocketOutputStream</p>
<p>日期类：DateFormat，DateTimeDateFormat，System（currentTimeMillis）</p>
<p>文件类：FileWatchdog（doOnChange）</p>
<p>内存操作类：基于引用计数机制的智能指针 ObjectPtrT<br>字符串操作类：StrictMath，StringHelper（toUpperCase，toLowerCase，trim，equalsIgnoreCase，endsWith，format），StringTokenizer</p>
<p>线程类：Thread（start，run，join）<br><br>　　使用以上的类不用考虑 thread handle, event handle, socket handle 之类的 handle 问题，所有这些文<br>件
已经被封装了。很好用，对不对？不足之处在于没有 GUI 类。ANSI C++ 中对于目录等文件系统的处理功能较弱，这里面也没有目录处理类。另外
Socket 的 read(void * buf, size_t len) 不能设置 timeout，并且如果读取数据个数小于 len 那么
read 函数将一直堵塞，不太好用，很可惜。实际的使用上面，可以考虑做一个 Socket 子类，重写 read() 函数。</p>
<p>vc71环境中编译安装<br>版本：log4cpp-0.3.5rc1.tar.gz</p>
<p>打开 msvc6 编译即可</p>
<p>头文件在 %log4cpp_root%/include\log4cpp<br>库文件在 %log4cpp_root%/lib</p>
<p>头文件添加方法如：<br>#include 需要链接lib库</p>
<p>６ Crypto++ 加/解密算法库<br>---<a  href="http://sourceforge.net/projects/cryptopp/" target="_blank">http://sourceforge.net/projects/cryptopp/</a><br>---<a  href="http://www.eskimo.com/%7Eweidai/cryptlib.html" target="_blank">http://www.eskimo.com/~weidai/cryptlib.html</a><br>---<a  href="http://www.cryptopp.com/" target="_blank">http://www.cryptopp.com</a></p>
<p>　　提供处理密码，消息验证，单向hash，公匙加密系统等功能的免费库。</p>
<p>　　Crypto++ 是一个非常专业的C++ 密码学函式库，几乎在密码学里头常见的演算法都可以在Crypto++ <br>找到实作的函式，如：block 与stream ciphers，hash functions，MACs，random number generators，public key 加密...等方法</p>
<p>vc71环境中编译安装<br>版本：cryptopp521.zip</p>
<p>直接通过 cryptest.dsw 相关的库</p>
<p>头文件在 %cryptopp_root%<br>库文件在 %cryptopp_root%/lib</p>
<p>头文件添加方法如：<br>#include &lt;*.h&gt; 需要链接lib库</p>
<p>７ ACE</p>
<p>------<a  href="http://www.cs.wustl.edu/%7Eschmidt/ACE.html" target="_blank">http://www.cs.wustl.edu/~schmidt/ACE.html</a></p>
<p>　　C+ +库的代表，超重量级的网络通信开发框架。ACE自适配通信环境（Adaptive Communication
Environment）是可以自由使用、开放源代码的面向对象框架，在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++
包装外观（Wrapper Facade）和框架组件，可跨越多种平台完成通用的通信软件任务，其中包括：</p>
<p>　　事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等等。</p>
<p>８. CppUnit<br>-------<a  href="http://sourceforge.net/projects/cppuint/" target="_blank">http://sourceforge.net/projects/cppuint/</a></p>
<p>　　一个c++的单元测试框架，可以通过派生测试类的方式，定制具体的测试方案。xUnit家族的一员，从JUnit移植而来，JUnit是Java语言的单元测试框架。</p>
<p>vc71环境中编译安装<br>版本：cppunit-1.10.2.tar.gz</p>
<p>直接通过 CppUnitLibraries.dsw 编译相关的库</p>
<p>头文件在 %cppunit_root%/cppunit<br>库文件在 %cppunit_root%/lib</p>
<p>头文件添加方法如：<br>#include 需要链接lib库</p>
<p>９ Loki<br>-----<a  href="http://moderncppdesign.com/" target="_blank">http://moderncppdesign.com</a><br>-----<a  href="http://sourceforge.net/projects/loki-lib/" target="_blank">http://sourceforge.net/projects/loki-lib/</a><br>-----<a  href="http://sourceforge.net/projects/loki-exp/" target="_blank">http://sourceforge.net/projects/loki-exp/</a></p>
<p>　　其实可和Boost一起介绍它，一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。</p>
<p>　　该库系模板库，库本身无需编译，在你的工程文件中 引用头文件就可以使用，如果 你直接或间接使用了small
object，那你需要在你的工程文件加上SmallObj.cpp；如果 你直接或间接使用了Singletons，那你需要在你的工程文件 加上
Singleton.cpp</p>
<p><strong>学术性的C++库的详细介绍</strong>:<br><br>1 FC++: The Functional C++ Library<br>--------<a  href="http://www.cc.gatech.edu/%7Eyannis/fc++/" target="_blank">http://www.cc.gatech.edu/~yannis/fc++/</a></p>
<p>　　这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣，可以去看看函数式程序设计的世界。大师Peter Norvig在 &#8220;Teach Yourself Programming in <br>Ten Years&#8221;一文中就将函数式语言列为至少应当学习的6类编程语言之一。</p>
<p>当前版本：FC++.1.5.zip<br>模板库,在实际工程中 ，加上要用的头文件 就可以编译。</p>
<p>2 CGAL<br>-----<a  href="http://www.cgal.org/" target="_blank">http://www.cgal.org</a></p>
<p>　　Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方<br>法以C++库的形式提供给工业和学术界的用户。</p>
<p>当前版本：CGAL-3.1.zip<br>这是一个已编译的版本，当然也包括完整的源码</p>
<p>头文件在 %CGAL_root%/include/CGAL<br>库文件在 %CGAL_root%/lib/msvc7</p>
<p>头文件添加方法如：<br>#include 需要链接lib库</p>
<p><strong>其它目前我感觉还不是很爽的C++库</strong><strong>的详细介绍</strong>:<br><br>1 Doxygen<br>------<a  href="http://sourceforge.net/projects/doxygen/" target="_blank">http://sourceforge.net/projects/doxygen/</a><br>------<a  href="http://www.stack.nl/%7Edimitri/doxygen/" target="_blank">http://www.stack.nl/~dimitri/doxygen/</a></p>
<p>　　注释文档生成工具，较之Doc++功能更为齐全，可以生成包括HTML、PDF、RTF在内的多种格式的文档，并有GUI界面，除了支持c/c++语言外，还支持IDL、java、PHP、c#等。</p>
<p>2、 QT(windows版要付钱) <br>-------<a  href="http://www.trolltech.com/" target="_blank">http://www.trolltech.com/</a><br>-------<a  href="http://www.qiliang.net/qt.html" target="_blank">http://www.qiliang.net/qt.html</a></p>
<p>　　Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功
能。Qt是完全面向对象的很容易扩展，并且允许真正地组件编程。自从1996年早些时候，Qt进入商业领域，它已经成为全世界范围内数千种成功的应用程序
的基础。Qt也是流行的Linux桌面环境KDE 的基础，同时它还支持Windows、Macintosh、Unix/X11等多种平台。</p>
<p>3、Fox<br>---------<a  href="http://www.fox-toolkit.org/" target="_blank">http://www.fox-toolkit.org/</a></p>
<p>　　开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受<br>出发，从而开始了对这个库的开发。有兴趣的可以尝试一下。</p>
<p>4 xml4c<br>------<a  href="http://www.alphaworks.ibm.com/tech/xml4c" target="_blank">http://www.alphaworks.ibm.com/tech/xml4c</a></p>
<p>　　IBM的XML Parser，用c++语言写就，功能超级强大。号称支持多达100种字符编码，能够支持中文，适合于大规模的xml应用。若只是很小范围的应用，则非最佳选择，毕竟，你需要&#8220;背负&#8221;约12M左右的dll的沉重负担。</p>
<p>5 Xerces c++<br>-------<a  href="http://xml.apache.org/xerces-c" target="_blank">http://xml.apache.org/xerces-c</a></p>
<p>　　Apache的XML项目，同样是c++ 实现，来源于IBM的xml4c，因此编程接口也是和xml4c一致的。但是目前只支持少数的字符编码，如ASCII，UTF-8，UTF-16等，不能处理包含中文字符的XML文档。</p>
<p>　　Xerces-C++ 是一个非常健壮的XML解析器，它提供了验证，以及SAX和DOM
API。XML验证在文档类型定义(Document Type
Definition，DTD)方面有很好的支持，并且在2001年12月增加了支持W3C XML Schema的基本完整的开放标准。</p>
<p>6 XMLBooster<br>-------<a  href="http://www.xmlbooster.com/" target="_blank">http://www.xmlbooster.com/</a></p>
<p>　　这个库通过产生特制的parser的办法极大的提高了XML解析的速度，并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。</p>
<p><strong>C++开发环境(Win平台下除了 Visual C++ 和 Borland C++以外的)： </strong></p>
<p>1. Cygwin （Windows下的一个Unix仿真环境）</p>
<p>　　这个Cygwin的一部分是GCC的另外一个Windows移植版本，Cygwin是Windows下的一个Unix仿真环境。严格的说是模拟GNU的环境，这也就是"Gnu's Not Unix"要表达的意思。</p>
<p>　　至Cygwin的網站<a  href="http://www.cygwin.com/" target="_blank">http://www.cygwin.com/</a>下載安裝程式setup.exe，可直接點選執行或先行下載至個人電腦後再執行。目前我已经下载到本地了，直接安装即可。</p>
<p>2. MinGW （GCC的一个Windows移植版本）</p>
<p>1)<a  href="http://sourceforge.net/projects/mingw" target="_blank">http://sourceforge.net/projects/mingw</a> 直接访问的，点击 Files，然后下载以下文件：MinGW-3.1.0-1.exe, mingw32-make-3.80.0-3.exe。<br>安装MinGW 到 C:/MinGW 目录下，然后安装 mingw32-make 到 C:/MinGW 下，通过浏览器<br>到 C:/MinGW/bin 下，将 mingw32-make.exe 改名或者另外复制为 make.exe。</p>
<p>（以上的设置已经足够。不过为了求新，我是同时下载了
gcc-core-3.4.2-20040916-1.tar.gz,mingw-runtime-3.5.tar.gz 和
w32api-3.1.tar.gz，将它们直接解压到 C:/MinGW
下更新旧的文件。不过这对这篇文章本身没有任何影响。新旧两种配置我都测试过。）</p>
<p>安装次序： <br>MinGW-3.1.0-1.exe<br>mingw32-make-3.80.0-3.exe<br>gcc-core-3.4.2-20040916-1.tar.gz<br>mingw-runtime-3.5.tar.gz<br>w32api-3.1.tar.gz<br>gdb-5.2.1-1.exe<br>mingw-utils-0.3.tar.gz<br>binutils-2.15.91-20040904-1.tar.gz</p>
<p>3)准备MinGW 用户开发的命令行环境（一个批处理）<br>如： mingw.bat<br>@rem --------------------------------------<br>@SET MINGW_ROOT=D:\Mingw</p>
<p>@rem<br>@echo Setting environment for using Mingw.<br>@rem</p>
<p>@set PATH=%MINGW_ROOT%\BIN;%PATH%<br>@set
INCLUDE=%MINGW_ROOT%\INCLUDE;%MINGW_ROOT%\INCLUDE\c++\3.2.3;%MINGW_ROOT%\include\c++\3.2.3\mingw32;%MINGW_ROOT%\include\c++\3.2.3\backward;%INCLUDE%<br>@set LIB=MINGW_ROOT\LIB;%LIB%<br>@rem ----------------------------------------</p>
<p>3. Dev C++ （一个C/C++ 的集成开发环境）</p>
<p>　　GCC是一个很好的编译器。在Windows上的C++编译器一直和标准有着一段距离的时候，GCC就是一个让Windows下开发者流口水的
编译器。Dev-C++就是能够让GCC跑在Windows下的工具，作为集成开发环境，还提供了同专业IDE相媲美的语法高亮，代码提示，调试等功能。
由于使用Delphi开发，占用内存少，速度很快，比较适合轻量级的学习和使用。</p>
<p>　　可以使用 MinGW-GCC 作为它的编译器</p>
<p>4 Eclipse-CDT</p>
<p>游戏开发</p>
<p>Audio/Video 3D C++ Programming Library</p>
<p>------<a  href="http://www.galacticasoftware.com/products/av/" target="_blank">http://www.galacticasoftware.com/products/av/</a><br>------<a  href="http://sourceforge.net/projects/av3d/" target="_blank">http://sourceforge.net/projects/av3d/</a></p>
<p>　　AV3D是一个跨平台，高性能的C++库。主要的特性是提供3D图形，声效支持（SB,以及S3M），控制接口（键盘，鼠标和遥感），XMS。</p>
<p>KlayGE</p>
<p>------<a  href="http://home.g365.net/enginedev/" target="_blank">http://home.g365.net/enginedev/</a><br>------<a  href="http://sourceforge.net/projects/klayge/" target="_blank">http://sourceforge.net/projects/klayge/</a></p>
<p>　　国内游戏开发高手自己用C++开发的一个开放源代码、跨平台的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎，并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。</p>
<p>OGRE</p>
<p>------<a  href="http://www.ogre3d.org/" target="_blank">http://www.ogre3d.org</a><br>------<a  href="http://www.ogre3d.org/docs/manual/" target="_blank">http://www.ogre3d.org/docs/manual/</a><br>------<a  href="http://sourceforge.net/projects/ogre" target="_blank">http://sourceforge.net/projects/ogre</a></p>
<p>　　OGRE（面向对象的图形渲染引擎）是用C++开发的，使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备
的应用程序或游戏。引擎中的类库对更底层的系统库（如：Direct3D和OpenGL）的全部使用细节进行了抽象，并提供了基于现实世界对象的接口和其
它类。</p><img src ="http://www.cppblog.com/xmli/aggbug/74860.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-02-25 10:06 <a href="http://www.cppblog.com/xmli/archive/2009/02/25/74860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用十年学习编程（转）</title><link>http://www.cppblog.com/xmli/archive/2009/01/20/72371.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Tue, 20 Jan 2009 06:34:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2009/01/20/72371.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/72371.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2009/01/20/72371.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/72371.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/72371.html</trackback:ping><description><![CDATA[<p>为什么每个人都急不可耐？</p>
<p>走进任何一家书店，你会看见《Teach Yourself Java in 7 Days》（7天Java无师自<br>通）的旁边是一长排看不到尽头的类似书籍，它们要教会你Visual Basic、Windows<br>、Internet等等，而只需要几天甚至几小时。我在Amazon.com上进行了如下搜索：<br>　　　　pubdate: after 1992 and title: days and (title: learn or title: teach<br>&nbsp;yourself)<br>　　　　(出版日期：1992年后 and 书名：天 and （书名：学会 or 书名：无师自<br>通）)<br>我一共得到了248个搜索结果。前面的78个是计算机书籍（第79个是《Learn Bengali<br>&nbsp;in 30 days》，30天学会孟加拉语）。我把关键词&#8220;days&#8221;换成&#8220;hours&#8221;，得到了<br>非常相似的结果：这次有253本书，头77本是计算机书籍，第78本是《Teach Yourself<br>&nbsp;Grammar and Style in 24 Hours》（24小时学会文法和文体）。头200本书中，有<br>96%是计算机书籍。<br>结论是，要么是人们非常急于学会计算机，要么就是不知道为什么计算机惊人地简单<br>，比任何东西都容易学会。没有一本书是要在几天里教会人们欣赏贝多芬或者量子物<br>理学，甚至怎样给狗打扮。<br>让我们来分析一下像《Learn Pascal in Three Days》（3天学会Pascal）这样的题<br>目到底是什么意思：<br><br>学会：在3天时间里，你不够时间写一些有意义的程序，并从它们的失败与成功中学<br>习。你不够时间跟一些有经验的程序员一起工作，你不会知道在那样的环境中是什么<br>滋味。简而言之，没有足够的时间让你学到很多东西。所以这些书谈论的只是表面上<br>的精通，而非深入的理解。如Alexander Pope（英国诗人、作家，1688-1744）所言<br>，一知半解是危险的（a little learning is a dangerous thing）</p>
<p>Pascal：在3天时间里你可以学会Pascal的语法（如果你已经会一门类似的语言），<br>但你无法学到多少如何运用这些语法。简而言之，如果你是，比如说一个Basic程序<br>员，你可以学会用Pascal语法写出Basic风格的程序，但你学不到Pascal真正的优点<br>（和缺点）。那关键在哪里？Alan Perlis（ACM第一任主席，图灵奖得主，1922-1990<br>）曾经说过：&#8220;如果一门语言不能影响你对编程的想法，那它就不值得去学&#8221;。另一<br>种观点是，有时候你不得不学一点Pascal（更可能是Visual Basic和javascript之类<br>）的皮毛，因为你需要接触现有的工具，用来完成特定的任务。但此时你不是在学习<br>如何编程，你是在学习如何完成任务。</p>
<p>3天：不幸的是，这是不够的，正如下一节所言。</p>
<p>10年编程无师自通</p>
<p>一些研究者（Hayes、Bloom）的研究表明，在许多领域，都需要大约10 年时间才能<br>培养出专业技能，包括国际象棋、作曲、绘画、钢琴、游泳、网球，以及神经心理学<br>和拓扑学的研究。似乎并不存在真正的捷径：即使是莫扎特，他4 岁就显露出音乐天<br>才，在他写出世界级的音乐之前仍然用了超过13年时间。再看另一种音乐类型的披头<br>士，他们似乎是在1964年的Ed Sullivan节目中突然冒头的。但其实他们从1957年就<br>开始表演了，即使他们很早就显示出了巨大的吸引力，他们第一次真正的成功——Sgt<br>. Peppers——也要到1967年才发行。Samuel Johnson（英国诗人）认为10 年还是不<br>够的：&#8220;任何领域的卓越成就都只能通过一生的努力来获得；稍低一点的代价也换不<br>来。&#8221;（Excellence in any department can be attained only by the labor of<br>&nbsp;a lifetime; it is not to be purchased at a lesser price.） 乔叟（Chaucer<br>，英国诗人，1340-1400）也抱怨说：&#8220;生命如此短暂，掌握技艺却要如此长久。&#8221;<br>（the lyf so short, the craft so long to lerne.）<br>下面是我在编程这个行当里获得成功的处方：</p>
<p><br>对编程感兴趣，因为乐趣而去编程。确定始终都能保持足够的乐趣，以致你能够将10<br>年时间投入其中。</p>
<p>跟其他程序员交谈；阅读其他程序。这比任何书籍或训练课程都更重要。</p>
<p>编程。最好的学习是从实践中学习。用更加技术性的语言来讲，&#8220;个体在特定领域最<br>高水平的表现不是作为长期的经验的结果而自动获得的，但即使是非常富有经验的个<br>体也可以通过刻意的努力而提高其表现水平。&#8221;（p. 366），而且&#8220;最有效的学习要<br>求为特定个体制定适当难度的任务，有意义的反馈，以及重复及改正错误的机会。&#8221;<br>（p. 20-21）《Cognition in Practice: Mind, Mathematics, and Culture in Everyday<br>&nbsp;Life》（在实践中认知：心智、数学和日常生活的文化）是关于这个观点的一本有<br>趣的参考书。</p>
<p>如果你愿意，在大学里花上4年时间（或者再花几年读研究生）。这能让你获得一些<br>工作的入门资格，还能让你对此领域有更深入的理解，但如果你不喜欢进学校，（作<br>出一点牺牲）你在工作中也同样能获得类似的经验。在任何情况下，单从书本上学习<br>都是不够的。&#8220;计算机科学的教育不会让任何人成为内行的程序员，正如研究画笔和<br>颜料不会让任何人成为内行的画家&#8221;, Eric Raymond，《The New Hacker's Dictionary<br>》（新黑客字典）的作者如是说。我曾经雇用过的最优秀的程序员之一仅有高中学历<br>；但他创造出了许多伟大的软件，甚至有讨论他本人的新闻组，而且股票期权让他达<br>到我无法企及的富有程度（译注：指Jamie Zawinski，Xemacs和Netscape的作者）。</p>
<p><br>跟别的程序员一起完成项目。在一些项目中成为最好的程序员；在其他一些项目中当<br>最差的一个。当你是最好的程序员时，你要测试自己领导项目的能力，并通过你的洞<br>见鼓舞其他人。当你是最差的时候，你学习高手们在做些什么，以及他们不喜欢做什<br>么（因为他们让你帮他们做那些事）。</p>
<p>接手别的程序员完成项目。用心理解别人编写的程序。看看在没有最初的程序员在场<br>的时候理解和修改程序需要些什么。想一想怎样设计你的程序才能让别人接手维护你<br>的程序时更容易一些。<br><br>学会至少半打编程语言。包括一门支持类抽象（class abstraction）的语言（如Java<br>或C++），一门支持函数抽象（functional abstraction）的语言（如Lisp或ML），<br>一门支持句法抽象（syntactic abstraction）的语言（如Lisp），一门支持说明性<br>规约（declarative specification）的语言（如Prolog或C++模版），一门支持协程<br>（coroutine）的语言（如Icon或Scheme），以及一门支持并行处理（parallelism）<br>的语言（如Sisal）。</p>
<p>记住在&#8220;计算机科学&#8221;这个词组里包含&#8220;计算机&#8221;这个词。了解你的计算机执行一条<br>指令要多长时间，从内存中取一个word要多长时间（包括缓存命中和未命中的情况）<br>，从磁盘上读取连续的数据要多长时间，定位到磁盘上的新位置又要多长时间。（答<br>案在这里。）</p>
<p>尝试参与到一项语言标准化工作中。可以是ANSI C++委员会，也可以是决定自己团队<br>的编码风格到底采用2个空格的缩进还是4个。不论是哪一种，你都可以学到在这门语<br>言中到底人们喜欢些什么，他们有多喜欢，甚至有可能稍微了解为什么他们会有这样<br>的感觉。</p>
<p>拥有尽快从语言标准化工作中抽身的良好判断力。</p>
<p><br>抱着这些想法，我很怀疑从书上到底能学到多少东西。在我第一个孩子出生前，我读<br>完了所有&#8220;怎样&#8230;&#8230;&#8221;的书，却仍然感到自己是个茫无头绪的新手。30个月后，我第<br>二个孩子出生的时候，我重新拿起那些书来复习了吗？不。相反，我依靠我自己的经<br>验，结果比专家写的几千页东西更有用更靠得住。<br>Fred Brooks在他的短文《No Silver Bullets》（没有银弹）中确立了如何发现杰出<br>的软件设计者的三步规划：</p>
<p>&nbsp;</p>
<p>尽早系统地识别出最好的设计者群体。</p>
<p>指派一个事业上的导师负责有潜质的对象的发展，小心地帮他保持职业生涯的履历。</p>
<p><br>让成长中的设计师们有机会互相影响，互相激励。</p>
<p><br>这实际上是假定了有些人本身就具有成为杰出设计师的必要潜质；要做的只是引导他<br>们前进。Alan Perlis说得更简洁：&#8220;每个人都可以被教授如何雕塑；而对米开朗基<br>罗来说，能教给他的倒是怎样能够不去雕塑。杰出的程序员也一样&#8221;。<br>所以尽管去买那些Java书；你很可能会从中找到些用处。但你的生活，或者你作为程<br>序员的真正的专业技术，并不会因此在24小时、24天甚至24个月内发生真正的变化。<br></p>
<p>参考文献</p>
<p>Bloom, Benjamin (ed.) Developing Talent in Young People, Ballantine, 1985<br>.<br>Brooks, Fred, No Silver Bullets, IEEE Computer, vol. 20, no. 4, 1987, p.<br>10-19.<br>Hayes, John R., Complete Problem Solver, Lawrence Erlbaum, 1989.<br>Lave, Jean, Cognition in Practice: Mind, Mathematics, and Culture in Everyday<br>&nbsp;Life, Cambridge University Press, 1988.</p>
<p>&nbsp;</p>
<p>答案</p>
<p>各种操作的计时，2001年夏天在一台典型的1GHz PC上完成：<br>　　　　执行单条指令　　　　　　　　　　　　 1 纳秒 = (1/1,000,000,000) 秒</p>
<p>　　　　从L1缓存中取一个word　　　　　　　　2 纳秒<br>　　　　从主内存中取一个word　　　　　　　　10 纳秒<br>　　　　从连续的磁盘位置中取一个word　　　　200 纳秒<br>　　　　从新的磁盘位置中取一个word（寻址）　8,000,000纳秒 = 8毫秒</p>
<p>&nbsp;</p>
<p>脚注</p>
本文的日文译本要感谢Yasushi Murakawa，中文译本要感谢郭晓刚，西班牙文译本要<br>感谢Carlos Rueda，德文译本要感谢Stefan Ram。<br>T. Capey指出Amazon上面《Complete Problem Solver》的页面中，《Teach Yourself<br>&nbsp;Bengali in 21 days》和《Teach Yourself Grammar and Style》被列在了&#8220;购买<br>此书的顾客还买了以下书籍&#8221;栏目里面。我猜其中一大部分察看这两本书的人都是从<br>我这里过去的。<img src ="http://www.cppblog.com/xmli/aggbug/72371.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-01-20 14:34 <a href="http://www.cppblog.com/xmli/archive/2009/01/20/72371.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>python IDE比较与推荐(转)</title><link>http://www.cppblog.com/xmli/archive/2008/11/19/67270.html</link><dc:creator>李现民</dc:creator><author>李现民</author><pubDate>Wed, 19 Nov 2008 05:06:00 GMT</pubDate><guid>http://www.cppblog.com/xmli/archive/2008/11/19/67270.html</guid><wfw:comment>http://www.cppblog.com/xmli/comments/67270.html</wfw:comment><comments>http://www.cppblog.com/xmli/archive/2008/11/19/67270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xmli/comments/commentRss/67270.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xmli/services/trackbacks/67270.html</trackback:ping><description><![CDATA[我先给一个初步的表格吧，大家如果有什么意见，或有补充，欢迎提出。有些我没有用过，先不写了。<br>以下是我使用过的python
IDE:<br><br>┌─────┬────┬────┬──┬────┬──┬─────┬─────┐ <br>│IDE name
&nbsp;│自动补全│智能感知│调试│语法检查│开源│特别注意 &nbsp;│ &nbsp;推荐度 &nbsp;│
<br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤ <br>│IDLE &nbsp; &nbsp; &nbsp;│手动 &nbsp; &nbsp;│有(很差)│用库│无 &nbsp;
&nbsp; &nbsp;│开源│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│★★ &nbsp; &nbsp; &nbsp;│ <br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤
<br>│PythonWin │手动　　│有 &nbsp; &nbsp; &nbsp;│用库│无 &nbsp; &nbsp; &nbsp;│开源│Win Only &nbsp;│★★★ &nbsp;
&nbsp;│<br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤<br>│SPE &nbsp; &nbsp; &nbsp; │无 &nbsp; &nbsp; &nbsp;│有 &nbsp; &nbsp;
&nbsp;│WPDB│存盘时 &nbsp;│开源│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│★★★★★│ <br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤
<br>│Ulipad &nbsp; &nbsp;│有 &nbsp; &nbsp; &nbsp;│有 &nbsp; &nbsp; &nbsp;│WPDB│存盘时 &nbsp;│开源│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│★★★★★│
<br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤ <br>│Eric &nbsp; &nbsp; &nbsp;│有 &nbsp; &nbsp; &nbsp;│有 &nbsp; &nbsp;
&nbsp;│类VC│存盘时 &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; &nbsp;
&nbsp;│ &nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│(个人推荐)│ <br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤ &nbsp;<br>│BOA
&nbsp; &nbsp; &nbsp; │手动　　│手动 &nbsp; &nbsp;│类VC│无 &nbsp; &nbsp; &nbsp;│开源│中文支持差│★★★ &nbsp; &nbsp;│
<br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤ <br>│WingIDE &nbsp; │有 &nbsp; &nbsp; &nbsp;│有 &nbsp; &nbsp;
&nbsp;│类VC│手动 &nbsp; &nbsp;│共享│中文要设置│★★★★ &nbsp;│ <br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤
<br>│Komodo &nbsp; &nbsp;│无 &nbsp; &nbsp; &nbsp;│有 &nbsp; &nbsp; &nbsp;│类VC│手动 &nbsp; &nbsp;│共享│相当耗资源│★★★★ &nbsp;│
<br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤ <br>│VIM+插件 &nbsp;│有　　　│无 &nbsp; &nbsp; &nbsp;│无 &nbsp;│无 &nbsp;
&nbsp; &nbsp;│开源│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│★★★ &nbsp; &nbsp;│ <br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤
<br>│emacs+插件│有　　　│无 &nbsp; &nbsp; &nbsp;│无 &nbsp;│无 &nbsp; &nbsp; &nbsp;│开源│ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│★★★ &nbsp; &nbsp;│
<br>├─────┼────┼────┼──┼────┼──┼─────┼─────┤ <br>│eclipse+ &nbsp;│手动 &nbsp; &nbsp;│有 &nbsp; &nbsp;
&nbsp;│类VC│手动 &nbsp; &nbsp;│开源│比较耗资源│★★★★ &nbsp;│ <br>│pydev插件 │ &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;│ <br>├─────┴─┬──┼────┼──┼────┼──┼─────┼─────┤
<br>│VS.Net 2003 &nbsp; │无 &nbsp;│有(很差)│类VC│无 &nbsp; &nbsp; &nbsp;│共享│兼容性很差│★(基本不 │ <br>│+VisualPython │
&nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; &nbsp;│ &nbsp; &nbsp;│ &nbsp; &nbsp; &nbsp; &nbsp;│ &nbsp; &nbsp;│已停止维护│能用) &nbsp; &nbsp; │
<br>└───────┴──┴────┴──┴────┴──┴─────┴─────┘<br><br>除了PythonWin,
VisualPython只支持Windows，其它都至少支持Win/Linux/Mac。<br>各项含义：<br>自动补全：变量/函数名打到一半时，提示可能的完整的变量/函数名。<br>智能感知：在库/类/对象后打"."后，提示可能的函数或变量。<br>调试：分四档，从好用到不好用分别为&#8220;类VC&#8221;(调试器操作方式与VC/eclipse相似)，&#8220;WPDB&#8221;(使用WinPdb作为调试器)，&#8220;用库&#8221;(要配合专门的python调试库，即要改代码来配合调试)，最惨的当然是&#8220;无&#8221;啦。<br>语法检查：从好用到不好用分别为&#8220;存盘时&#8221;(存盘时自动检查，也可以在菜单里手动选择检查)，&#8220;手动&#8221;(在菜单里选择检查)，&#8220;无&#8221;(没有语法检查功能)<br>开源：分为开源，共享(提供免费试用，然后需要付费)，收费三种。目前还没有&#8220;收费&#8221;这一类。<br>推荐度：五星为最推荐，一星为最不推荐。推荐度为作者主观评价，不代表其他人意见。<br><br>各IDE简介(注意本文最后修改时间是2008年7月)：<br>IDLE:<br>装了python就会有这个，大家肯定都用过了，功能还凑合，调试器的使用方法和大家熟悉的eclipse/Visual
Studio很不一样，需要学习和适应。各项表现都一般。推荐度：★★<br>PythonWin:<br>内置Win32
extension，PythonWin成为了win32的python程序开发者必备的工具。虽然它只能运行在Win下，但其实也是开源的。功能上可以认为它是加上了自动补全和智能感知功能的IDLE，虽然和以其它一些复杂的IDE相比有些差距，但却是轻量级Python
IDE的首选。推荐度★★★<br>SPE:<br>全名Stani's Python
Editor。相当不错的IDE，语法高亮、代码折叠、智能感知、自动语法检查等功能一应俱全，集成wxGlade。可惜没有自动补全功能。开源，可以用svn下载到最新的源代码，依赖wxPython。推荐度★★★★★<br>附：总有人说下不到SPE，去这里看看：<br><a  href="http://developer.berlios.de/project/showfiles.php?group_id=4161" target="_blank">http://developer.berlios.de/project/showfiles.php?group_id=4161</a><br>SVN方式下载：<br><a  href="http://pythonide.blogspot.com/2007/02/how-to-download-latest-spe-from_26.html" target="_blank">http://pythonide.blogspot.com/2007/02/how-to-download-latest-spe-from_26.html</a><br>Ulipad:<br>前身是NewEdit，和SPE相比，多了自动补全功能，因而比SPE更加方便，不过没有把界面设计器wxGlade集成进来。开源，可以用svn下载到最新的源代码，依赖wxPython。推荐度★★★★★。<br>Eric:<br>Eric升级到4后，各方面有了很强的提升，全方位超过其它开源IDE。使用PyQt4作为图形库，界面美观大方，并与QtDesigner结合，使得开发GUI程序变得非常方便，比下面将提到到BOA还要好用。最大的亮点莫过于它的调试器，支持断点设置、单步调试和变量值查看。一句话，有了Eric4，就不用再去捣腾商业的IDE了。推荐度★★★★★，个人强烈推荐。<br>Eric4在Windows下的安装有些要注意的地方，参见：<br><a  href="http://hi.baidu.com/runningon/blog/item/091dd009c4c80187d1581b05.html" target="_blank">http://hi.baidu.com/runningon/blog/item/091dd009c4c80187d1581b05.html</a><br>Boa
Constructor:<br>比起SPE和Ulipad，BOA的编辑功能相当单薄，自动补全与智能感知都要手动，而且没有自动语法检查，但调试器相当好用。最大的亮点是界面设计器相当好用，比wxGlade要好用得多。硬伤是对中文支持不好。依赖wxPython。推荐度★★★。<br>WingIDE:<br>很不错的商业软件，调试器是类VC/eclipse的，相当好用，而且还支持project组织。但默认的设置是不支持中文的，要设置一下字体。不开源，而且破解不好找。既然已经有了好用的开源软件了，又何必再用盗版的呢？推荐度★★★★<br>Komodo:<br>由ActiveState公司制作，该公司的ActivePython和ActivePerl可是相当有名。Komodo和WingIDE一样也是很不错的商业软件，可以说WingIDE有的大部分优点Komodo也有，非常可惜没有自动补全。还支持宏录制(类似MS
Office的宏录制)，不过这也只是个噱头。不爽之处在于相当耗资源，我AMD
64位双核+1G内存+5400转的硬盘的本本，启动它时硬盘闪了足足一分钟。400$的价格对它来讲贵了点。推荐度★★★★<br>VIM/emacs +
插件:<br>Linux我也玩了好几年了，平心而论这两个东东不适合初学(的开发)者。现在Linux已经比较好用了，不会vim/emacs也不是什么大不了的事了，特别是后者。推荐度★★★。<br>eclipse
+
pydev:<br>依靠强大的eclipse，pydev显得格外耀眼。强大的调试功能和舒服的编辑环境让pydev赢得了许多人的青睐。不过eclipse本来就是耗资源大户，pydev在这一点上毫无办法。能配好pydev需要一些人品，用最新的eclipse，在线安装最新pydev(pydev的网站上会有介绍)，成功率会高一些。推荐度★★★★<br>VisualStudio.Net
2003 + VisualPython:<br>已经停止维护了，烂就一个字，多说无益，基本不能用。推荐度★<br><img src ="http://www.cppblog.com/xmli/aggbug/67270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xmli/" target="_blank">李现民</a> 2008-11-19 13:06 <a href="http://www.cppblog.com/xmli/archive/2008/11/19/67270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>