﻿<?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++博客-游戏人生-随笔分类-T技术碎语</title><link>http://www.cppblog.com/Fox/category/6273.html</link><description>游戏人生 != ( 人生 == 游戏 )</description><language>zh-cn</language><lastBuildDate>Thu, 20 Nov 2008 19:40:36 GMT</lastBuildDate><pubDate>Thu, 20 Nov 2008 19:40:36 GMT</pubDate><ttl>60</ttl><item><title>设计模式（三）&amp;mdash;&amp;mdash;Singleton</title><link>http://www.cppblog.com/Fox/archive/2008/11/19/67339.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 19 Nov 2008 15:37:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/11/19/67339.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/67339.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/11/19/67339.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/67339.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/67339.html</trackback:ping><description><![CDATA[<p>不知道Singleton算不算用的最多的，平时用的时候，往往都是直接敲下面一段：</p><code> <p>&#8230;&#8230;</p> <p>不是不想改，就是懒，敲多了已经不觉得这么写多浪费时间了，按大家的说法，这样写至少有这么几个缺点：</p> <p>1. 必须在程序结束前手动释放，这不仅是RP问题，如果你借了内存不主动还，说明你RP差，但被别人搞丢了（宕机）导致你还不上，说明别人RP差？所以，这还是个问题；</p> <p>2. 线程同步问题，如果Singleton实例跨线程使用，上例不安全，在Initial和Release时加锁可以解决；</p> <p>3. 最大的问题：不能重用。</p> <p><a href="http://www.yulefox.com/index.php/20081119/design-patterns-03.html/" target="_blank">阅读全文</a></p></code><img src ="http://www.cppblog.com/Fox/aggbug/67339.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-11-19 23:37 <a href="http://www.cppblog.com/Fox/archive/2008/11/19/67339.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式（二）&amp;mdash;&amp;mdash;State</title><link>http://www.cppblog.com/Fox/archive/2008/11/19/67251.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 18 Nov 2008 16:57:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/11/19/67251.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/67251.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/11/19/67251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/67251.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/67251.html</trackback:ping><description><![CDATA[<p><strong>State模式</strong>对应到C++的<strong>多态</strong>特性。</p> <p><strong>State模式</strong>适用较广，这儿给出比较常见易懂的三种情况：</p> <p>1. 当怪物在面对不同职业和特性的玩家时对应不同的AI处理和技能释放：</p><code> <p>CSkill* pAttackSkill;</p> <p>if( pPlayer-&gt;MagicImmune() ) pAttackSkill = SomePhysicalAttackSkill;</p> <p>else if( pPlayer-&gt;PhysicalImmune() ) pAttackSkill = SomeMagicAttackSkill;</p> <p>...</p> <p>pAttackSkill-&gt;Begin();</p> <p>...</p></code> <p>或者使用分支结构：</p><code> <p>CSkill* pAttackSkill;</p> <p>switch( pPlayer-&gt;GetOccupation() ) </p> <p>{</p> <p>&nbsp; case WARRIOR: pAttackSkill = SomeLongRangeSkill; break;</p> <p>&nbsp; case MAGICIAN: pAttackSkill = SomeForceSkill; break;</p> <p>&nbsp; case NIMROD: pAttackSkill = SomeMagicSkill; break;</p> <p>...</p> <p>}</p> <p>pAttackSkill-&gt;Begin();</p> <p>...</p></code> <p><a href="http://www.yulefox.com/index.php/20081119/design-patterns-02.html/" target="_blank">阅读全文</a></p><img src ="http://www.cppblog.com/Fox/aggbug/67251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-11-19 00:57 <a href="http://www.cppblog.com/Fox/archive/2008/11/19/67251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WSAEventSelect剖析</title><link>http://www.cppblog.com/Fox/archive/2008/10/27/65263.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 27 Oct 2008 15:25:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/10/27/65263.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/65263.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/10/27/65263.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/65263.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/65263.html</trackback:ping><description><![CDATA[<p>本文同时发布在<a title="http://www.yulefox.com" href="http://www.yulefox.com" target="_blank" rel="tag">http://www.yulefox.com</a>和<a title="http://www.cppblog.com/fox" href="http://www.cppblog.com/fox" target="_blank" rel="tag">http://www.cppblog.com/fox</a>。 <p>近来在Windows下用WSAEventSelect时，碰到一个棘手的问题，当然现在已经解决了。  <p><strong>问题描述：</strong>  <p>一个Server，一个ClientA，一个ClientB，Server用WSAEventSelect模型监听（只有监听，没有读写），ClientA在连接Server后，ClientA对应的EventA被触发，Server的WSAWaitForMultipleEvents等待到EventA，ClientB连接Server时，TCP三次握手成功，ClientB与Server的TCP状态被置为ESTABLISHED，然而Server的WSAWaitForMultipleEvents没有等待到EventB被触发。  <p>用netstat看了一下，ClientB与Server的状态是ESTABLISHED，此时如果ClientB退出，由于Server无法正常Close该连接，因此Server的状态不是TIME_WAIT而是CLOSE_WAIT（持续2小时），Client的状态是FIN_WAIT_2（持续10分钟）。  <p>我尝试将ClientA主动关闭后再次连接Server，Server的WSAWaitForMultipleEvents在wait到EventA之后，EventB此时也被触发。  <p>开始一直以为问题的根源在于WSAEventSelect的使用上，毕竟，之前没有系统写过类似的代码，难免怀疑到事件模型的使用上。多方查阅资料，最后还是没有发现类似问题的解决方案。  <p>又跟了一上午之后，Kevin开始怀疑是多线程使用的问题，我看了一下，的确没有对event的多线程操作进行处理，但因为在另一个应用中，使用了同样的模块，却没有该问题。最后考虑必要性时还是放弃了加临界资源，无视多线程同步问题。Kevin本来劝我换个模型，但我固执的认为要做就把这事儿做好。因为下午还要回学校一趟，就想尽快搞定，毕竟因为这一块已经把Kevin的进度拖了一周了，心下还是过意不去，而且隐约感觉到离问题的解决越来越近了。  <p><strong>问题分析：</strong>  <p>在对着WSAWaitForMultipleEvents思考了半天之后，忽然开窍了，如果ThreadA在WSAWaitForMultipleEvents时，只有一个EventA被WSAEventSelect并set到signaled状态，则该EventA会被wait成功，ThreadA处理EventA之后继续阻塞在WSAWaitForMultipleEvents。此时，ThreadB通过WSAEventSelect将EventB初始化为nonsignaled状态，之后即使EventB被set为signaled状态，但ThreadA的WSAWaitForMultipleEvents因为处于阻塞状态，不可能刷新事件集，也就不可能wait到EventB，最终导致了ClientB的请求无法被响应。如果EventA被触发则会被ThreadA等待到，WSAWaitForMultipleEvents返回后再次进入时事件集已经被刷新，EventB被wait到也就不难理解了。  <p><strong>问题解决：</strong>  <p>说到底是因为当ThreadA阻塞在WSAWaitForMultipleEvents处之时，事件集的变更无法立即得到体现。如果允许上层应用随时create或close一些event，则WSAWaitForMultipleEvents就不应该无限阻塞下去。  <p>因此最后的一个解决方法就是让WSAWaitForMultipleEvents超时返回并Sleep一段时间，当WSAWaitForMultipleEvents再次进入时事件集得以更新。  <p>想了一下，另一个应用中之所以没出现该问题也只是个巧合，因为该应用中ThreadB的两次WSAEventSelect间隔很短，在ThreadA获得时间片之前已经确定了事件集。  <p>说白了这也不是一个什么大问题，甚至谈不上任何难度，但是因为之前对WSAEventSelect没有一个清晰的概念，因此在发现和分析问题上花费了大量时间，加上在VS2005调试过程中，有个别文件更新时没有被重新编译，也耗费了很多无谓的时间，以至于我们都在考虑是不是要放弃IDE，因为我们确实太依赖IDE了，有些TX为了稳妥，每次都是“重新生成整个解决方案”，如果一个解决方案有几千个文件、几十万行的代码，估计重编一次也要花个几分钟吧。  <p><strong>总结：</strong>  <ol> <li>netstat观察的网络连接处于ESTABLISHED状态并不意味着逻辑连接被accept，只是表明客户端connect的TCP物理连接（三次握手）被服务器端ack，如果服务器没有accept到该连接，证明网络模块代码有问题；  <li>多线程怎么都是个问题，线程同步尽量避免，毕竟，用Kevin的话来说，加锁是丑陋的。但在涉及到同步问题时，还是权衡一下，我这儿之所以最后没有加临界区，是因为事件主要是在ThreadA中处理，ThreadB中只有create操作，而且ThreadA对事件集的刷新要求不是那么严格，也就不考虑加临界区了；  <li>如果能力和条件允许的话，放弃IDE吧，IDE的确不是个好东西，我主要是指在编译链接的时候，如果作为编辑器说不定还会好用:)。 </li></ol> <hr width="50%">  <p>个人网站<a title="http://www.yulefox.com" href="http://www.yulefox.com" target="_blank" rel="tag">http://www.yulefox.com</a>用的主机最近从据说要黑屏的Windows换成了Debian，还在调整，估计明天能弄好，内容肯定比Cppblog杂的多，谈点技术的还是会同步更新到<a title="http://www.cppblog.com/fox" href="http://www.cppblog.com/fox" target="_blank" rel="tag">http://www.cppblog.com/fox</a>。</p><img src ="http://www.cppblog.com/Fox/aggbug/65263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-10-27 23:25 <a href="http://www.cppblog.com/Fox/archive/2008/10/27/65263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>支持成员函数指针的消息映射机制的简单实现</title><link>http://www.cppblog.com/Fox/archive/2008/10/10/63625.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Fri, 10 Oct 2008 02:20:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/10/10/63625.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/63625.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/10/10/63625.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/63625.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/63625.html</trackback:ping><description><![CDATA[<p>作者：Fox</p> <p>本文同时发布在<a href="http://www.yulefox.com">http://www.yulefox.com</a>和<a href="http://www.cppblog.com/fox">http://www.cppblog.com/fox</a>。</p> <p>十天之前，在CPPBLOG上写了一篇<a title="消息映射机制的简单实现" href="http://www.cppblog.com/Fox/archive/2008/09/29/63056.html" target="_blank" rel="tag">消息映射机制的简单实现</a>，有同学提到该实现不支持成员函数。这个问题我也考虑到了，既然被提出来，不妨把实现提供出来。</p> <p>需要说明的是，我本身对template比较不感冒，不过<a title="Kevin Lynx" href="http://www.cppblog.com/kevinlynx/archive/2008/03/17/44678.html" target="_blank" rel="tag">Kevin Lynx</a>对template感冒，而且写过关于成员函数指针的问题，想了很久，如果支持成员函数指针，不用模板是不行了。</p> <p>此处对成员函数的支持还不涉及对函数参数的泛化，因为我这个消息映射暂时不需要参数泛化，下面的代码应该不需要过多的解释了。</p><code> <p>#define REG_MSG_FUNC(nMsgType, MsgFunc) \ <br>&nbsp;&nbsp;&nbsp; CMsgRegister::RegisterCallFunc(nMsgType, MsgFunc); </p> <p>#define REG_MSG_MEM_FUNC(nMsgType, Obj, MsgFunc) \ <br>&nbsp;&nbsp;&nbsp; CMsgRegister::RegisterCallFunc(nMsgType, Obj, MsgFunc); </p> <p>class CBaseMessage; </p> <p>class CHandler <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; virtual int operator()(CBaseMessage* pMsg) = 0; <br>}; </p> <p>template&lt;typename FuncType&gt; <br>class CDefHandler : public CHandler <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; CDefHandler(){} <br>&nbsp;&nbsp;&nbsp; CDefHandler(FuncType &amp;Func) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : m_Func(Func) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; virtual int operator()(CBaseMessage* pMsg) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return m_Func(pMsg); <br>&nbsp;&nbsp;&nbsp; } </p> <p>protected: <br>&nbsp;&nbsp;&nbsp; FuncType&nbsp;&nbsp;&nbsp; m_Func; <br>}; </p> <p>template&lt;typename ObjType, typename FuncType&gt; <br>class CMemHandler : public CHandler <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; CMemHandler(){} <br>&nbsp;&nbsp;&nbsp; CMemHandler(ObjType* pObj, FuncType Func) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : m_pObj(pObj) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , m_Func(Func) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; virtual int operator()(CBaseMessage* pMsg) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (m_pObj-&gt;*m_Func)(pMsg); <br>&nbsp;&nbsp;&nbsp; } </p> <p>protected: <br>&nbsp;&nbsp;&nbsp; FuncType&nbsp;&nbsp;&nbsp; m_Func; <br>&nbsp;&nbsp;&nbsp; ObjType*&nbsp;&nbsp;&nbsp; m_pObj; <br>}; </p> <p>class CFunction <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; CFunction() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : m_pHandler(NULL) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; // 封装(C函数或静态成员函数) <br>&nbsp;&nbsp;&nbsp; template&lt;typename FuncType&gt; <br>&nbsp;&nbsp;&nbsp; CFunction( FuncType &amp;Func ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : m_pHandler(new CDefHandler&lt;FuncType&gt;(Func)) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; // 封装(非静态成员函数) <br>&nbsp;&nbsp;&nbsp; template&lt;typename ObjType, typename FuncType&gt; <br>&nbsp;&nbsp;&nbsp; CFunction( ObjType* pObj, FuncType Func ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : m_pHandler(new CMemHandler&lt;ObjType, FuncType&gt;(pObj, Func)) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; virtual ~CFunction() <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DELETE_SAFE(m_pHandler); <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 函数调用 <br>&nbsp;&nbsp;&nbsp; int operator()(CBaseMessage* pMsg) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (*m_pHandler)(pMsg); <br>&nbsp;&nbsp;&nbsp; } </p> <p>private: <br>&nbsp;&nbsp;&nbsp; CHandler&nbsp;&nbsp;&nbsp; *m_pHandler; <br>}; </p> <p>typedef std::map&lt;int, CFunction*&gt; MSG_MAP; <br>typedef MSG_MAP::iterator MSG_ITR; </p> <p>class CMsgRegister <br>{ <br>public: <br>&nbsp;&nbsp;&nbsp; // 注册消息函数(C函数或静态成员函数) <br>&nbsp;&nbsp;&nbsp; template &lt;typename FuncType&gt; <br>&nbsp;&nbsp;&nbsp; inline static void RegisterCallFunc(int nMsgType, FuncType &amp;Func) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFunction *func = new CFunction(Func); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_MsgMap[nMsgType] = func; <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; // 注册消息函数(非静态成员函数) <br>&nbsp;&nbsp;&nbsp; template &lt;typename ObjType, typename FuncType&gt; <br>&nbsp;&nbsp;&nbsp; inline static void RegisterCallFunc(int nMsgType, ObjType* pObj, FuncType Func) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFunction *func = new CFunction(pObj, Func); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_MsgMap[nMsgType] = func; <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; // 执行消息 <br>&nbsp;&nbsp;&nbsp; inline static void RunCallFunc(int nMsgType, CBaseMessage* pMsg) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSG_ITR itr = s_MsgMap.find(nMsgType); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( s_MsgMap.end() != itr ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*itr-&gt;second)(pMsg); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; } </p> <p>&nbsp;&nbsp;&nbsp; static void ReleaseMsgMap()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 释放消息映射表 <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSG_ITR itr = s_MsgMap.begin(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while( itr != s_MsgMap.end() ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DELETE_SAFE(itr-&gt;second); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itr = s_MsgMap.erase(itr); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; } </p> <p>protected: <br>&nbsp;&nbsp;&nbsp; static MSG_MAP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_MsgMap;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息映射表 <br>};</p></code> <p>不可否认，模板给了你更大的想象空间，很多东西，还是不要一味排斥的好:)。</p><img src ="http://www.cppblog.com/Fox/aggbug/63625.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-10-10 10:20 <a href="http://www.cppblog.com/Fox/archive/2008/10/10/63625.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消息映射机制的简单实现</title><link>http://www.cppblog.com/Fox/archive/2008/09/29/63056.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 29 Sep 2008 10:34:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/09/29/63056.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/63056.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/09/29/63056.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/63056.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/63056.html</trackback:ping><description><![CDATA[<p>项目中使用了消息通信机制，因为消息类型非常多，相应的，处理消息的地方代码也非常多。  <p>自然而然想到MFC中的消息映射：  <p>创建一个缺省MFC框架程序的解决方案Test，在Test.h中看到以下内容： <code> <p>class Ctest_mfcApp : public CWinApp<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; Ctest_mfcApp();  <p>// 重写<br>public:<br>&nbsp;&nbsp;&nbsp; virtual BOOL InitInstance();  <p>// 实现<br>&nbsp;&nbsp;&nbsp; afx_msg void OnAppAbout();<br>&nbsp;&nbsp;&nbsp; DECLARE_MESSAGE_MAP()<br>};  <p>&nbsp;</code></p> <p>其中，最紧要的就是<code>DECLARE_MESSAGE_MAP()</code>这个宏，相关内容展开如下： <code> <p>struct AFX_MSGMAP_ENTRY<br>{<br>&nbsp;&nbsp;&nbsp; UINT nMessage;&nbsp;&nbsp; // windows message<br>&nbsp;&nbsp;&nbsp; UINT nCode;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // control code or WM_NOTIFY code<br>&nbsp;&nbsp;&nbsp; UINT nID;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // control ID (or 0 for windows messages)<br>&nbsp;&nbsp;&nbsp; UINT nLastID;&nbsp;&nbsp;&nbsp; // used for entries specifying a range of control id's<br>&nbsp;&nbsp;&nbsp; UINT_PTR nSig;&nbsp;&nbsp; // signature type (action) or pointer to message #<br>&nbsp;&nbsp;&nbsp; AFX_PMSG pfn;&nbsp;&nbsp;&nbsp; // routine to call (or special value)<br>};  <p>struct AFX_MSGMAP<br>{<br>&nbsp;&nbsp;&nbsp; const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();<br>&nbsp;&nbsp;&nbsp; const AFX_MSGMAP_ENTRY* lpEntries;<br>};  <p>#define DECLARE_MESSAGE_MAP() \<br>protected: \<br>&nbsp;&nbsp;&nbsp; static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \<br>&nbsp;&nbsp;&nbsp; virtual const AFX_MSGMAP* GetMessageMap() const; \</code>  <p>其中<code>AFX_PMSG</code>不再解析下去，我们认为这是一个指向特定消息对应的实现函数的函数指针，这几个宏的作用可简单理解成为Test这个项目定义了一个静态的消息映射表。当消息到来时，从消息队列中弹出消息并分发到具有入口实现的上层CWnd派生窗口。用户只需要注册消息，实现消息入口函数就够了，这在MFC中一般放在.cpp文件头部。Test.cpp中头部有以下内容： <code> <p>BEGIN_MESSAGE_MAP(CTest, CWinApp)<br>&nbsp;&nbsp;&nbsp; ON_COMMAND(ID_APP_ABOUT, &amp;CTest::OnAppAbout)<br>&nbsp;&nbsp;&nbsp; // 基于文件的标准文档命令<br>&nbsp;&nbsp;&nbsp; ON_COMMAND(ID_FILE_NEW, &amp;CWinApp::OnFileNew)<br>&nbsp;&nbsp;&nbsp; ON_COMMAND(ID_FILE_OPEN, &amp;CWinApp::OnFileOpen)<br>&nbsp;&nbsp;&nbsp; // 标准打印设置命令<br>&nbsp;&nbsp;&nbsp; ON_COMMAND(ID_FILE_PRINT_SETUP, &amp;CWinApp::OnFilePrintSetup)<br>END_MESSAGE_MAP()</code>  <p>这里是为消息枚举值与消息实现函数建立映射，其中涉及到的宏的展开如下：</p><code> <p>#define ON_COMMAND(id, memberFxn) \<br>&nbsp;&nbsp;&nbsp; { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static_cast&lt;AFX_PMSG&gt; (memberFxn) },  <p>#define BEGIN_MESSAGE_MAP(theClass, baseClass) \<br>&nbsp;&nbsp;&nbsp; PTM_WARNING_DISABLE \<br>&nbsp;&nbsp;&nbsp; const AFX_MSGMAP* theClass::GetMessageMap() const \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { return GetThisMessageMap(); } \<br>&nbsp;&nbsp;&nbsp; const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \<br>&nbsp;&nbsp;&nbsp; { \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef theClass ThisClass;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef baseClass TheBaseClass;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static const AFX_MSGMAP_ENTRY _messageEntries[] =&nbsp; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {  <p>#define END_MESSAGE_MAP() \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \<br>&nbsp;&nbsp;&nbsp; }; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static const AFX_MSGMAP messageMap = \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { &amp;TheBaseClass::GetThisMessageMap, &amp;_messageEntries[0] }; \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return &amp;messageMap; \<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; \<br>&nbsp;&nbsp;&nbsp; PTM_WARNING_RESTORE  <p></code>按照上述思路得到的相似代码如下：</p><code> <p>// Test.h<br>typedef void (* funCall)(void*);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息执行函数类型  <p>struct tagMsgEntry&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息入口结构<br>{<br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nMsgType;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息类型<br>&nbsp;&nbsp;&nbsp; funCall&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MsgRun;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息执行函数<br>};  <p>struct tagMsgMap&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>&nbsp;&nbsp;&nbsp; const tagMsgMap* (__stdcall* funGetBaseMsgMap)();<br>&nbsp;&nbsp;&nbsp; const tagMsgEntry* pMsgEntries;&nbsp;&nbsp;&nbsp;&nbsp; // 消息入口集<br>};  <p>class CMessage<br>{<br>&nbsp;&nbsp;&nbsp; // ...<br>protected:<br>&nbsp;&nbsp;&nbsp; static const tagMsgMap* __stdcall GetThisMsgMap();<br>&nbsp;&nbsp;&nbsp; virtual const tagMsgMap* GetMsgMap() const;<br>};  <p>// Test.cpp<br>const tagMsgMap* CMessage::GetMsgMap() const<br>{<br>&nbsp;&nbsp;&nbsp; return GetThisMsgMap();<br>}  <p>const tagMsgMap* __stdcall CMessage::GetThisMsgMap()<br>{<br>&nbsp;&nbsp;&nbsp; static const tagMsgEntry MsgEntries[] =<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { MSG_SOME_ONE, SomeOneFunc },<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { MSG_SOME_TWO, SomeTwoFunc },<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { MSG_NULL, NULL }<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; static const tagMsgMap msgMap =<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;CBaseMessage::GetThisMsgMap,&nbsp;&nbsp;&nbsp; // 父类消息映射表<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;MsgEntries[0]<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; return &amp;msgMap;<br>} </p> <p>int CMessage::MsgProc(int nMsgType)<br>{<br>&nbsp;&nbsp;&nbsp; switch( nMsgType )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; case MSG_SOME_ONE:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return CBaseMessage::MsgProc(nMsgType);<br>}</code>  <p>这种处理的优点在于，子类没有定义的消息实现接口，可以使用父类接口实现。不过在现在的消息处理中，我们一般不需要由基类来完成，因此可以不依赖基类接口，使用宏可以使代码看上去更加简洁。  <p>___________________________________________________________  <p>简化版本的消息映射采用以下方式，简单清晰：<code>  <p>// Test.h<br>#define REG_MSG_FUNC(nMsgType, MsgFunc) \<br>&nbsp;&nbsp;&nbsp; CMessge::RegisterCallFunc(nMsgType, MsgFunc); \  <p>typedef void (* function)(void*);  <p>typedef std::map&lt;int, function&gt; MSG_MAP;<br>typedef MSG_MAP::const_iterator MSG_CITR;  <p>class CMessage<br>{<br>&nbsp;&nbsp;&nbsp; // ...<br>public:<br>&nbsp;&nbsp;&nbsp; static const MSG_MAP&amp; GetMsgMap();<br>&nbsp;&nbsp;&nbsp; static void RegisterCallFunc(int nMsgType, void(* Func)(void *))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_MsgMap[nMsgType] = Func;<br>&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp; int CMessage::Run(int nMsgType)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息公用执行函数<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSG_ITR itr = s_MsgMap.find(nMsgType);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( s_MsgMap.end() != itr )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itr-&gt;second(this);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }  <p>protected:<br>&nbsp;&nbsp;&nbsp; static MSG_MAP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_MsgMap;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 消息映射表<br>};  <p>// UserTest.cpp -- 用户在使用时对自己关心的消息予以注册, 入口函数予以实现即可<br>REG_MSG_FUNC(MSG_SOME_ONE, SomeOneFunc)  <p>void SomeOneFunc(CBaseMessage *pMsg)<br>{<br>&nbsp;&nbsp;&nbsp; return;<br>}</code>  <p>___________________________________________________________  <p>最近忙的焦头烂额，正好写到消息，稍微整理一下，提供些许借鉴。</p><img src ="http://www.cppblog.com/Fox/aggbug/63056.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-09-29 18:34 <a href="http://www.cppblog.com/Fox/archive/2008/09/29/63056.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再辨同步/异步与阻塞/非阻塞</title><link>http://www.cppblog.com/Fox/archive/2008/09/11/61555.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 10 Sep 2008 17:11:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/09/11/61555.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/61555.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/09/11/61555.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/61555.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/61555.html</trackback:ping><description><![CDATA[<p>网络编程学习和实践的过程中，<strong>同步（synchronous）/异步（asynchronous）</strong>与<strong>阻塞（blocking）/非阻塞（non-blocking）</strong>总是会迷惑很多人。依然记得我半年之前在<a title="MMORPG网络模型剖析&mdash;&mdash;IOCP篇" href="http://www.cppblog.com/Fox/archive/2008/03/06/mmorpg_net_iocp.html" target="_blank">记述IOCP</a>时，一句不经意的“<strong>非阻塞I/O则是致力于提供高效的异步I/O</strong>”便引来一番口水论争。</p> <p>今天在查一些资料的时候，看到关于这几个词的论辩竟不是一般的多，细细想来，这个问题似乎也确实有解释的必要，不在于争论对错，而在于辨明是非。</p> <p>讨论之前，先限定讨论的范围：<strong>此处之同步/异步仅限于I/O操作</strong>，与OS所讨论的进程/线程中的<strong>其他同步/异步</strong>没有直接关系；讨论的内容是：<strong>两对相似的术语之间的区别到底有多大</strong>。</p> <ul> <li><strong>非常大：</strong></li></ul> <p>Douglas C. Schmidt在《C++网络编程》中这样说到：</p> <p>They are very different, as follows:</p> <p>AIO is "asynchronous I/O", i.e., the operation is invoked asynchronously and control returns to the client while the OS kernel processes the I/O request.&nbsp; When the operation completes there is some mechanism for the client to retrieve the results.</p> <p>Non-blocking I/O tries an operation (such as a read() or write()) and if it the operation would block (e.g., due to flow control on a TCP connection or due to lack of data in a socket), the call returns -1 and sets errno to EWOULDBLOCK.</p> <p>翻译如下：</p> <p><a title="异步I/O" href="http://en.wikipedia.org/wiki/Asynchronous_input/output" target="_blank" rel="tag">异步I/O</a>：例如，操作被异步调用时，控制权交给客户端，I/O操作请求则交由操作系统内核处理，当操作完成后，通过某种机制将结果通知客户端。</p> <p>非阻塞I/O：尝试调用某操作，如果操作被阻塞，则调用返回-1并置错误值为EWOULDBLOCK。</p> <p>从这两段“very different”的解释来看，我的感觉是并没有指出二者的区别，因为我们无法确定所谓AIO是如何处理的，如果AIO直接“调用返回-1并置错误值为EWOULDBLOCK”，实现“控制权交给客户端”，似乎并无任何不妥。况且，对于非阻塞I/O，我们也需要“当操作完成后，通过某种机制将结果通知客户端”这样的处理。</p> <ul> <li><strong>无差别：</strong></li></ul> <p>而在<a title="Wikipedia" href="http://www.wikipedia.org/" target="_blank">Wikipedia</a>上则直接等同二者：<b>Asynchronous I/O</b>, or <b>non-blocking I/O</b>, is a form of <a title="I/O" href="http://en.wikipedia.org/wiki/Input/output" target="_blank">input/output</a> processing that permits other processing to continue before the transmission has finished.</p> <p>当然，对于recv和send，我们一般会说他们是<strong>阻塞</strong>起的，而不会说他们是<strong>同步</strong>起的，但这显然不是二者的区别，因为我们都知道，<strong>阻塞的原因正是等待同步结果的返回</strong>。</p> <p>因此，二者的区别在于，<strong>阻塞/非阻塞是表现，同步/异步是原因</strong>，我们说某某操作是阻塞起的，或者某某线程是阻塞起的，是因为在等待操作结果的同步返回；我们说某某操作是非阻塞的，是因为操作结果会通过异步方式返回。</p> <p>讨论到这儿，再咬文嚼字的争辩下去似乎已经没有任何实际意义。</p> <p>------------------------------------------------------------</p> <p>PS：纠结一些必要的概念是为了加深理解，太过纠结了反倒会滞塞理解。我之前对于其概念也并非特别清楚，所以才会再续一篇特意言明，也算弥补一下自己的过失。</p><img src ="http://www.cppblog.com/Fox/aggbug/61555.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-09-11 01:11 <a href="http://www.cppblog.com/Fox/archive/2008/09/11/61555.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式（一）</title><link>http://www.cppblog.com/Fox/archive/2008/08/06/58154.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 06 Aug 2008 07:43:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/08/06/58154.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/58154.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/08/06/58154.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/58154.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/58154.html</trackback:ping><description><![CDATA[<p><strong><font color=#800000>0. Introduction</font></strong></p>
<p>接触设计模式有两年时间了，但一直没有系统整理过，为了不至于让自己的思维被繁琐的工作一点点禁锢，还是决定总结一下，为了能够真正做到有所收获，整个系列会按照<a title="Gang of Four" href="http://en.wikipedia.org/wiki/Gang_of_Four_%28software%29" target=_blank>GoF的Design Patterns: Elements of Reusable Object-Oriented Software</a>的行文思路，但不会照本宣科就是了，<a title=Wikipedia href="http://www.wikipedia.org/" target=_blank>Wikipedia</a>上关于23种设计模式的介绍非常全面，CSDN上也可以下载中/英文电子档，因此很多套话、类图一概省去。</p>
<p>最早接触设计模式的时候，难免被各种模式的联系和区别所困扰，从教科书的分析可以得到模式之间形式上的不同。但这样对于领会设计模式意义不大，因为我们掌握模式的目的是为了融会贯通，灵活运用，以对开发有所帮助。</p>
<p>稍微成规模的OO程序，会有大量对象，其中很多实体对象之间存在着<strong>父子、兄弟</strong>关系，对象的<strong>创建</strong>提升为一种模式。其好处在于设计模式本身所宣称的<strong>reusable</strong>，这就像堆积木盖房子一样，堆的好的情况下，换一换门窗便是另一番风景。</p>
<p>关于实现，我不会为了厘清模式间的区别而刻意使用相似代码实现，相反，我会根据模式本身的适用情况举例，而且大量代码基于<a title=http://sourcemaking.com/design_patterns href="http://sourcemaking.com/design_patterns" target=_blank>SourceMaking</a>。</p>
<p>_______________________________</p>
<p><strong><font color=#800000>1. Creational Design Patterns(DP)</font></strong></p>
<p>创建型DP抽象了类和对象的创建过程，<a title="Gang of Four" href="http://en.wikipedia.org/wiki/Gang_of_Four_%28software%29" target=_blank>GoF</a>给出了5种<strong>创建型DP</strong>：<strong>Abstract Factory</strong>、<strong>Builder</strong>、<strong>Factory Method</strong>、<strong>Builder</strong>、<strong>Prototype</strong>、<strong>Singleton</strong>。</p>
<p><strong><font color=#800000>2. Abstract Factory</font></strong></p>
<p><strong>意图：提供一个创建一系列相关或相互依赖对象的接口，而无需指定它们具体的类。</strong></p>
<p>1) 只提供了一个创建接口，其返回值为具体产品：如<code>AbstractProduct *Client::CreateProduct(AbstractFactory &amp;factory);</code></p>
<p>2) 接口的参数是一个<strong>工厂对象</strong>（<code>AbstractFactory &amp;factory</code>）的引用，参数类型（<code>AbstractFactory</code>）为抽象基类，调用时根据需要传入具体工厂对象即可；</p>
<p>3) 接口内部实现了<strong>一系列相关或相互依赖对象</strong>（抽象产品）的创建：当传入具体工厂时，接口实现的就是<strong>一系列具体产品</strong>的创建；</p>
<p>4) 创建的产品<strong>立即返回</strong>（<code>CreateProduct</code>）。</p>
<p><strong>参与者：</strong></p>
<p>&#8226; AbstractFactory<br>— 声明一个创建抽象产品对象的操作接口。</p>
<p>&#8226; ConcreteFactory<br>— 实现创建具体产品对象的操作。</p>
<p>&#8226; AbstractProduct<br>— 为一类产品对象声明一个接口。</p>
<p>&#8226; ConcreteProduct<br>— 定义一个将被相应的具体工厂创建的产品对象。<br>— 实现AbstractProduct接口。</p>
<p>&#8226; Client<br>— 仅使用由AbstractFactory和AbstractProduct类声明的接口。</p>
<p><strong>代码：</strong></p>
<code>
<p>class AbstractFactory<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual AbstractProduct *MakePartA() = 0;<br>&nbsp;&nbsp;&nbsp; virtual AbstractProduct *MakePartB() = 0;<br>&nbsp;&nbsp;&nbsp; virtual AbstractProduct *MakePartC() = 0;<br>&nbsp;&nbsp;&nbsp; virtual AbstractProduct *AddPart(const AbstractProduct *pPart) = 0;<br>};
<p>AbstractProduct *Client::CreateProduct(AbstractFactory &amp;factory)<br>{<br>&nbsp;&nbsp;&nbsp; AbstractProduct *pProduct = factory.CreateProduct();<br>&nbsp;&nbsp;&nbsp; AbstractProduct *pPartA = factory.MakePartA();<br>&nbsp;&nbsp;&nbsp; AbstractProduct *pPartB = factory.MakePartB();<br>&nbsp;&nbsp;&nbsp; AbstractProduct *pPartC = factory.MakePartC();<br>&nbsp;&nbsp;&nbsp; factory.AddPart(pPartA);<br>&nbsp;&nbsp;&nbsp; factory.AddPart(pPartB);<br>&nbsp;&nbsp;&nbsp; factory.AddPart(pPartC);<br>&nbsp;&nbsp;&nbsp; return pProduct;<br>} </p>
<p>int main(void)<br>{<br>&nbsp;&nbsp;&nbsp; Client client;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; ConcreteFactory factory;<br>&nbsp;&nbsp;&nbsp; client.CreateProduct(factory);<br>&nbsp;&nbsp;&nbsp; return 0;<br>}</code>
<p><strong><font color=#800000>3. Builder</font></strong></p>
<p><strong>意图：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示。</strong></p>
<p>1) director提供抽象产品创建接口：如<code>void Director::Construct();</code></p>
<p>2) 不同产品使用<strong>同一创建过程</strong>，由director指定特定builder以生产不同产品；</p>
<p>3) 接口内部实现了<strong>一个复杂对象</strong>（抽象产品）的创建：当传入具体工厂时，接口实现的是<strong>一个复杂的具体产品</strong>的创建；</p>
<p>4) 创建的产品<strong>并不立即返回</strong>，<strong>创建完毕</strong>后返回，或<strong>使用接口</strong>（<code>GetProduct</code>）提取结果。</p>
<p><strong>参与者：</strong></p>
<p>&#8226; Builder<br>— 为创建一个Product对象的各个部件指定抽象接口。</p>
<p>&#8226; ConcreteBuilder<br>— 实现Builder的接口以构造和装配该产品的各个部件。<br>— 定义并明确它所创建的表示。<br>— 提供一个检索产品的接口。</p>
<p>&#8226; Director<br>— 构造一个使用Builder接口的对象。</p>
<p>&#8226; Product<br>— 表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。<br>— 包含定义组成部件的类，包括将这些部件装配成最终产品的接口。</p>
<p><strong>代码：</strong></p>
<code>
<p>class Builder<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual void MakePartA() = 0;<br>&nbsp;&nbsp;&nbsp; virtual void MakePartB() = 0;<br>&nbsp;&nbsp;&nbsp; virtual void MakePartC() = 0;
<p>&nbsp;&nbsp;&nbsp; Product *GetProduct()&nbsp;&nbsp;&nbsp; { return _product; }
<p>protected:<br>&nbsp;&nbsp;&nbsp; Product *_product;<br>};
<p>class Director<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; void setBuilder(Builder *b)&nbsp;&nbsp;&nbsp; { _builder = b; }<br>&nbsp;&nbsp;&nbsp; void Construct();
<p>private:<br>&nbsp;&nbsp;&nbsp; Builder *_builder;<br>};
<p>void Director::Construct()<br>{<br>&nbsp;&nbsp;&nbsp; _builder.MakePartA();<br>&nbsp;&nbsp;&nbsp; _builder.MakePartB();<br>&nbsp;&nbsp;&nbsp; _builder.MakePartC();<br>}
<p>int main(void) {<br>&nbsp;&nbsp;&nbsp; ConcreteBuilderA concreteBuilderA;<br>&nbsp;&nbsp;&nbsp; ConcreteBuilderB concreteBuilderB;<br>&nbsp;&nbsp;&nbsp; Director director;<br>&nbsp;&nbsp;&nbsp; Product *pProduct;
<p>&nbsp;&nbsp;&nbsp; director.SetBuilder(&amp;concreteBuilderA);<br>&nbsp;&nbsp;&nbsp; director.Construct();<br>&nbsp;&nbsp;&nbsp; pProduct = concreteBuilderA.GetProduct();<br>&nbsp;&nbsp;&nbsp; pProduct-&gt;Show();
<p>&nbsp;&nbsp;&nbsp; director.SetBuilder(&amp;concreteBuilderB);<br>&nbsp;&nbsp;&nbsp; director.Construct();<br>&nbsp;&nbsp;&nbsp; pProduct = concreteBuilderB.GetProduct();<br>&nbsp;&nbsp;&nbsp; pProduct-&gt;Show();
<p>&nbsp;&nbsp;&nbsp; return 0;<br></code>}</p>
<p><strong><font color=#800000>4. Factory Method</font></strong></p>
<p><strong>意图：定义一个用于创建对象的接口，让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。</strong></p>
<p>1) 看得出该模式其实就是<strong>C++的多态特性</strong>，借<strong>继承</strong>实现。因此，其别名为<strong>虚构造器（ Virtual Constructor）</strong>；</p>
<p>2) 作为模式与C++多态特性不同的是，Creator可以定义<strong>工厂方法的缺省实现</strong>，完成缺省操作，MFC大量使用了这一思想。</p>
<p><strong>参与者：</strong></p>
<p>&#8226; Product<br>— 定义工厂方法所创建的对象的接口。</p>
<p>&#8226; ConcreteProduct<br>— 实现Product接口。</p>
<p>&#8226; Creator<br>— 声明工厂方法，该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现，它返回一个缺省的ConcreteProduct对象。<br>— 可以调用工厂方法以创建一个Product对象。</p>
<p>&#8226; ConcreteCreator<br>— 重定义工厂方法以返回一个ConcreteProduct实例。</p>
<p><strong>代码：</strong></p>
<p><code>ConcreteProduct *ConcreteCreator::FactoryMethod()<br>{<br>&nbsp;&nbsp;&nbsp; <code>ConcreteProduct</code> *pProduct = new <code>ConcreteProduct</code>;<br>&nbsp;&nbsp;&nbsp; return pProduct;<br>}</code></p>
<p><code>Product *Creator::FactoryMethod()<br>{<br>&nbsp;&nbsp;&nbsp; Product *pProduct = new Product;<br>&nbsp;&nbsp;&nbsp; return pProduct;<br>} </p>
<p>int main(void) {<br>&nbsp;&nbsp;&nbsp; Creator creator;<br>&nbsp;&nbsp;&nbsp; ConcreteProduct *pProduct;
<p>&nbsp;&nbsp;&nbsp; pProduct = creator.FactoryMethod();<br>&nbsp;&nbsp;&nbsp; pProduct-&gt;Show();
<p>&nbsp;&nbsp;&nbsp; return 0;<br>}</code>
<p><strong><font color=#800000>5. Prototype</font></strong></p>
<p><strong>意图：用原型实例指定创建对象的种类，并且通过拷贝这些原型创建新的对象。</strong></p>
<p>1) 创建不再通过工厂新类<strong>继承（inheritance）</strong>，而是通过<strong>委托（delegation）</strong>；</p>
<p>2) 根通<strong>拷贝原型实例</strong>创建新对象。</p>
<p><strong>参与者：</strong></p>
<p>&#8226; ProtoType<br>— 声明一个克隆自身的接口。</p>
<p>&#8226; ConcreteProtoType<br>— 实现一个克隆自身的操作。</p>
<p>&#8226; Client<br>— 让一个原型克隆自身从而创建一个新的对象。</p>
<p><strong>代码：</strong></p>
<code>
<p>class ProtoType<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual void Draw();<br>&nbsp;&nbsp;&nbsp; virtual ProtoType *Clone() = 0;<br>&nbsp;&nbsp;&nbsp; virtual void Initialize();<br>};
<p>class ProtoTypeA: public ProtoType<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual ProtoType *Clone()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ProtoTypeA;<br>&nbsp;&nbsp;&nbsp; }<br>};
<p>class ProtoTypeB: public ProtoType<br>{ <br>public:<br>&nbsp;&nbsp;&nbsp; virtual ProtoType *Clone()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ProtoTypeB;<br>&nbsp;&nbsp;&nbsp; }<br>};
<p>class Client<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; static ProtoType *Clone( int choice );
<p>private:<br>&nbsp;&nbsp;&nbsp; static ProtoType *s_prototypes[3];<br>};
<p>ProtoType* Client::s_prototypes[] = { 0, new ProtoTypeA, new ProtoTypeB };
<p>ProtoType *Client::Clone(int choice)<br>{<br>&nbsp;&nbsp;&nbsp; return s_prototypes[choice]-&gt;Clone();<br>}</code>
<p>&#160;</p>
<p><strong><font color=#800000>6. Singleton</font></strong></p>
<p><strong>意图：保证一个类仅有一个实例，并提供一个访问它的全局访问点。</strong></p>
<p>1) 用<strong>静态成员函数</strong>保证上述意图。</p>
<p><strong>参与者：</strong></p>
<p>&#8226; Singleton<br>— 定义一个Instance操作，允许客户访问它的唯一实例。Instance是一个类操作（即C++中的一个静态成员函数）。<br>— 可能负责创建它自己的唯一实例。</p>
<p>&#160;</p>
<p><strong>代码：</strong></p>
<code>
<p>class Singleton<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; static Singleton *GetInstance()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!s_instance)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_instance = new Singleton;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return s_instance;<br>&nbsp;&nbsp;&nbsp; }
<p>&nbsp;&nbsp;&nbsp; void Run()&nbsp;&nbsp;&nbsp; {}
<p>private:<br>&nbsp;&nbsp;&nbsp; static Singleton *s_instance;<br>&nbsp;&nbsp;&nbsp; Singleton()&nbsp;&nbsp;&nbsp; {}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Singleton cannot be created outside.<br>};
<p>Singleton *GetSingleton(void)<br>{<br>&nbsp;&nbsp;&nbsp; return Singleton::GetInstance();<br>}
<p>int main(void)<br>{<br>&nbsp;&nbsp;&nbsp; GetSingleton()-&gt;Run();
<p>&nbsp;&nbsp;&nbsp; return 0;<br>}</p>
</code>
<p>______________________________________________</p>
<p>代码写的都比较简单，基本可以将各种模式之间的不同体现出来了。</p><img src ="http://www.cppblog.com/Fox/aggbug/58154.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-08-06 15:43 <a href="http://www.cppblog.com/Fox/archive/2008/08/06/58154.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Big-endian记忆（附闲扯）</title><link>http://www.cppblog.com/Fox/archive/2008/07/30/57508.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 30 Jul 2008 06:48:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/30/57508.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/57508.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/30/57508.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/57508.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/57508.html</trackback:ping><description><![CDATA[<p><strong><font color="#800000">一、Big-endian &amp; Little-endian</font></strong></p> <p>还是<a title="Wikipedia" href="http://www.wikipedia.org/" target="_blank">Wikipedia</a>好啊！可惜中文的国内看不了，愚昧啊！实在觉得中文有点难懂，看看<a title="Wikipedia 日本语版本" href="http://ja.wikipedia.org/" target="_blank">日本语版本</a>吧:D！</p> <p>关于<strong>端（endianness）</strong>的介绍，<a title="Wikipedia" href="http://www.wikipedia.org/" target="_blank">Wikipedia</a>上比较全了：<a title="http://en.wikipedia.org/wiki/Endianness" href="http://en.wikipedia.org/wiki/Endianness">http://en.wikipedia.org/wiki/Endianness</a></p> <p>关于<strong>网络字节序（network byte order）</strong>和<strong>主机字节序（host byte order）</strong>，说来挺无关紧要的一点东西，因为每次总是忘掉，所以每次都要好奇的看看<strong>大端（big-endian）</strong>和<strong>小端（little-endian）</strong>。</p> <p>给定<code>unsigned long</code>型整数十六进制形式：<tt>0x0A0B0C0D</tt>，其big-endian和little-endian形式分别为：</p> <p><strong>1) Big-endian</strong></p> <p><tt>Memory<br>| </tt><tt>...&nbsp; |&nbsp; 8-bit atomic element size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tt><tt>| </tt><tt>...&nbsp;&nbsp;&nbsp; |&nbsp; 16-bit atomic element size</tt><br><tt>| </tt><tt>0x0A |&nbsp; a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <tt>| </tt><tt>0x0A0B |&nbsp; a<br></tt></tt><tt>| </tt><tt>0x0B |&nbsp; a+1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <tt>| </tt><tt>0x0C0D |&nbsp; a+</tt>1</tt><br><tt>| </tt><tt>0x0C |&nbsp; a+2<br></tt><tt>| 0x0D |&nbsp; a+3<br></tt><tt>| ...&nbsp; |</tt></p> <p><strong>2) Little-endian（X86）</strong></p> <p><tt>Memory<br>| </tt><tt>...&nbsp; |&nbsp; 8-bit atomic element size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tt><tt>| </tt><tt>...&nbsp;&nbsp;&nbsp; |&nbsp; 16-bit atomic element size</tt><br><tt>| </tt><tt>0x0D |&nbsp; a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <tt>| </tt><tt>0x0C0D |&nbsp; a<br></tt></tt><tt>| </tt><tt>0x0C |&nbsp; a+1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <tt>| </tt><tt>0x0A0B |&nbsp; a+</tt>1</tt><br><tt>| </tt><tt>0x0B |&nbsp; a+2<br></tt><tt>| 0x0A |&nbsp; a+3<br></tt><tt>| ...&nbsp; |</tt></p> <p align="center"><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Endianessmap.svg/250px-Endianessmap.svg.png"> </p> <p align="center">Mapping registers to memory locations (from Wikipedia)</p> <p>为什么X86存储会使用little-endian，起初我想对于位运算，尤其是位移运算，little-endian很方便，但转念一想，big-endian也方便啊，无非是左移和右移的区别而已，但little-endian的优势在于unsigned char/short/int/long类型转换时，存储位置无需改变。</p> <p>在网络传输中，采用big-endian序，对于<tt>0x0A0B0C0D</tt>，传输顺序就是<tt>0A 0B 0C 0D</tt>，因此big-endian作为network byte order，little-endian作为host byte order。</p> <p>________________________________________________</p> <p><strong>PS：做鸡有什么不好？</strong></p> <p>上午跟某同事（为尊重虑，下文以Y称之）躲在犄角旮旯抽烟。以下为场景再现：</p> <p>（忽然整出来一句）Y：听过鹰的故事没有？</p> <p>（满脸疑惑）Fox：没有。</p> <p>Y：一只小鹰掉到鸡窝里，#$@%……</p> <p>F：我不是鹰，我就是一只鸡，做<strike>技术</strike>鸡有什么不好？</p> <p>Y：做技术没有不好啊……</p> <p>F：我不是说做技术，我说做鸡，我就是在地上走的，我为什么总是要抬头看天？</p> <p>Y：你要往上看，没有人注定不能飞，XX以前也没有想过有一天会飞起来。</p> <p>F：我不是掉到鸡窝里，我本来就在鸡窝里，我也喜欢呆在鸡窝里，别人都在地上走，我为什么要飞起来？</p> <p>Y：你总要飞起来。</p> <p>F：我说了我喜欢呆在鸡窝里，你见过有那只鸡飞起来了？</p> <p>Y：……</p> <p>F：我就是一只鸡，插了鸡翅还是飞不起来，况且，我对飞起来也没有任何兴趣。</p> <p>Y：……</p> <p>F：做鸡有什么不好？</p> <p>Y：你看老毛，与人斗其乐无穷，他境界多高，与天斗其乐无穷，知道吧，他已经不屑与人斗了。</p> <p>F：我不喜欢与人斗，我也斗不过，做鸡有什么不好？</p> <p>Y：……</p><img src ="http://www.cppblog.com/Fox/aggbug/57508.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-30 14:48 <a href="http://www.cppblog.com/Fox/archive/2008/07/30/57508.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（八）[完]</title><link>http://www.cppblog.com/Fox/archive/2008/07/23/56933.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 23 Jul 2008 06:28:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/23/56933.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56933.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/23/56933.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56933.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56933.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a>  <ul> <li><strong><font color="#800000">规则之例外</font></strong></li></ul> <p>前面说明的编码习惯基本是强制性的，但所有优秀的规则都允许例外。</p> <p><strong><font color="#800000">1. 现有不统一代码（Existing Non-conformant Code）</font></strong></p> <p>对于现有不符合既定编程风格的代码可以网开一面。</p> <p>当你修改使用其他风格的代码时，为了与代码原有风格保持一致可以不使用本指南约定。如果不放心可以与代码原作者或现在的负责人员商讨，记住，一致性包括原有的一致性。</p> <p><strong><font color="#800000">1. Windows代码（Windows Code）</font></strong></p> <p>Windows程序员有自己的编码习惯，主要源于Windows的一些头文件和其他Microsoft代码。我们希望任何人都可以顺利读懂你的代码，所以针对所有平台的C++编码给出一个单独的指导方案。</p> <p>如果你一直使用Windows编码风格的，这儿有必要重申一下某些你可能会忘记的指南<font color="#800000">（译者注，我怎么感觉像在被洗脑:D）</font>：</p> <p>1) 不要使用<font color="#800000">匈牙利命名法（Hungarian notation，如定义整型变量为<code>iNum</code>）</font>，使用Google命名约定，包括对源文件使用<code>.cc</code>扩展名；</p> <p>2) Windows定义了很多原有内建类型的同义词<font color="#800000">（译者注，这一点，我也很反感）</font>，如<code>DWORD</code>、<code>HANDLE</code>等等，在调用Windows API时这是完全可以接受甚至鼓励的，但还是尽量使用原来的C++类型，例如，使用<code>const TCHAR *</code>而不是<code>LPCTSTR</code>；</p> <p>3) 使用Microsoft Visual C++进行编译时，将<strong>警告级别设置为3或更高</strong>，并<strong>将所有warnings当作errors处理</strong>；</p> <p>4) 不要使用<code>#pragma once</code>;作为<strong>包含保护</strong>，使用C++标准<strong>包含保护</strong>，<strong>包含保护</strong>的文件路径包含到项目树顶层<font color="#800000">（译者注，<code>#include&lt;prj_name/public/tools.h&gt;</code>）</font>；</p> <p>5) 除非万不得已，否则不使用任何不标准的扩展，如<code>#pragma</code>和<code>__declspec</code>，允许使用<code>__declspec(dllimport)</code>和<code>__declspec(dllexport)</code>，但必须通过<code>DLLIMPORT</code>和<code>DLLEXPORT</code>等宏，以便其他人在共享使用这些代码时容易放弃这些扩展。</p> <p><font color="#800000"><font color="#000000">在Windows上，只有很少一些偶尔可以不遵守的规则：</font></font></p> <p>1) 通常我们<strong>禁止使用多重继承</strong>，但在使用<a title="Component Object Model (COM)" href="http://en.wikipedia.org/wiki/Component_Object_Model" target="_blank">COM</a>和<a title="Active Template Library (ATL)" href="http://en.wikipedia.org/wiki/Active_Template_Library" target="_blank">ATL</a>/<a title="Windows Template Library (WTL)" href="http://en.wikipedia.org/wiki/WTL" target="_blank">WTL</a>类时可以使用多重继承，为了执行<a title="Component Object Model (COM)" href="http://en.wikipedia.org/wiki/Component_Object_Model" target="_blank">COM</a>或<a title="Active Template Library (ATL)" href="http://en.wikipedia.org/wiki/Active_Template_Library" target="_blank">ATL</a>/<a title="Windows Template Library (WTL)" href="http://en.wikipedia.org/wiki/WTL" target="_blank">WTL</a>类及其接口时可以使用多重实现继承；</p> <p>2) 虽然<strong>代码中不应使用异常</strong>，但在<a title="Active Template Library (ATL)" href="http://en.wikipedia.org/wiki/Active_Template_Library" target="_blank">ATL</a>和部分<a title="Standard Template Library (STL)" href="http://en.wikipedia.org/wiki/Standard_Template_Library" target="_blank">STL</a>（包括Visual C++的STL）中异常被广泛使用，使用<a title="Active Template Library (ATL)" href="http://en.wikipedia.org/wiki/Active_Template_Library" target="_blank">ATL</a>时，应定义<code>_ATL_NO_EXCEPTIONS</code>以屏蔽异常，你要研究一下是否也屏蔽掉STL的异常，如果不屏蔽，开启编译器异常也可以，注意这只是为了编译STL，自己仍然不要写含异常处理的代码；</p> <p>3) 通常每个项目的每个源文件中都包含一个名为<code>StdAfx.h</code>或<code>precompile.h</code>的头文件方便头文件预编译，为了使代码方便与其他项目共享，避免显式包含此文件（<code>precompile.cc</code>除外），使用编译器选项<code>/FI</code>以自动包含；</p> <p>4) 通常名为<code>resource.h、且只包含宏</code>的资源头文件，不必拘泥于此风格指南。</p> <ul> <li><strong><font color="#800000">团队合作</font></strong></li></ul> <p>参考常识，<strong>保持一致</strong>。</p> <p>编辑代码时，花点时间看看项目中的其他代码并确定其风格，如果其他代码<code>if</code>语句中使用空格，那么你也要使用。如果其中的注释用星号（<code>*</code>）围成一个盒子状，你也这样做：</p><pre>/**********************************
* Some comments are here.
* There may be many lines.
**********************************/

</pre>
<p>编程风格指南的使用要点在于提供一个公共的编码规范，所有人可以把精力集中在实现内容而不是表现形式上。我们给出了全局的风格规范，但局部的风格也很重要，如果你在一个文件中新加的代码和原有代码风格相去甚远的话，这就破坏了文件本身的整体美观也影响阅读，所以要尽量避免。</p>
<p>好了，关于编码风格写的差不多了，代码本身才是更有趣的，尽情享受吧！</p>
<p align="right"><em><font face="Times New Roman">Benjy Weinberger<br>Craig Silverstein<br>Gregory Eitzmann<br>Mark Mentovai<br>Tashana Landray</font></em> </p>
<p>______________________________________ </p>
<p><strong>译者：终于翻完了，前后历时两周，整个过程中，虽因工作关系偶有懈怠，但总算不是虎头蛇尾（起码我的态度是非常认真的:D），无论是否能对你有所裨益，对我而言，至少是温习了一些以前知道的知识，也学到了一些之前不知道的知识</strong><strong>。</strong>
<p><strong>刚好这两天还不是特紧张，赶紧翻完了，要开始干活了……</strong><img src ="http://www.cppblog.com/Fox/aggbug/56933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-23 14:28 <a href="http://www.cppblog.com/Fox/archive/2008/07/23/56933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（七）</title><link>http://www.cppblog.com/Fox/archive/2008/07/23/56922.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 23 Jul 2008 03:43:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/23/56922.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56922.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/23/56922.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56922.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56922.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">格式</font></strong></li></ul> <p>代码风格和格式确实比较随意，但一个项目中所有人遵循同一风格是非常容易的，作为个人未必同意下述格式规则的每一处，但整个项目服从统一的编程风格是很重要的，这样做才能让所有人在阅读和理解代码时更加容易。</p> <p><strong><font color="#800000">1. 行长度（Line Length）</font></strong></p> <p>每一行代码字符数不超过80。</p> <p>我们也认识到这条规则是存有争议的，但如此多的代码都遵照这一规则，我们感觉一致性更重要。</p> <p><strong>优点</strong>：提倡该原则的人认为强迫他们调整编辑器窗口大小很野蛮。很多人同时并排开几个窗口，根本没有多余空间拓宽某个窗口，人们将窗口最大尺寸加以限定，一致使用80列宽，为什么要改变呢？</p> <p><strong>缺点</strong>：反对该原则的人则认为更宽的代码行更易阅读，80列的限制是上个世纪60年代的大型机的古板缺陷；现代设备具有更宽的显示屏，很轻松的可以显示更多代码。</p> <p><strong>结论</strong>：80个字符是最大值。例外：</p> <p>1) 如果一行注释包含了超过80字符的命令或URL，出于复制粘贴的方便可以超过80字符；</p> <p>2) 包含长路径的可以超出80列，尽量避免；</p> <p>3) 头文件保护（防止重复包含<a title="[译]Google C++编程风格指南（一）" href="http://www.cppblog.com/Fox/archive/2008/07/10/55818.html" target="_blank">第一篇</a>）可以无视该原则。</p> <p><strong><font color="#800000">2. 非ASCII字符（Non-ASCII Characters）</font></strong></p> <p>尽量不使用非ASCII字符，使用时必须使用<a title="UTF-8" href="http://en.wikipedia.org/wiki/UTF-8" target="_blank">UTF-8</a>格式。</p> <p>哪怕是英文，也不应将用户界面的文本硬编码到源代码中，因此非ASCII字符要少用。特殊情况下可以适当包含此类字符，如，代码分析外部数据文件时，可以适当硬编码数据文件中作为分隔符的非ASCII字符串；更常用的是（不需要本地化的）单元测试代码可能包含非ASCII字符串。此类情况下，应使用UTF-8格式，因为很多工具都可以理解和处理其编码，十六进制编码也可以，尤其是在增强可读性的情况下——如<code>"\xEF\xBB\xBF"</code>是Unicode的<a title="zero-width no-break space" href="http://en.wikipedia.org/wiki/Non-breaking_space" target="_blank">zero-width no-break space</a>字符，以UTF-8格式包含在源文件中是不可见的。</p> <p><strong><font color="#800000">3. 空格还是制表位（Spaces vs. Tabs）</font></strong></p> <p>只使用空格，每次缩进2个空格。</p> <p>使用空格进行缩进，不要在代码中使用tabs，设定编辑器将tab转为空格。</p> <p><strong>译者注：在前段时间的</strong><a title="Debian开发学习日记（二）：系统简单配置" href="http://www.cppblog.com/Fox/archive/2008/06/06/debian_simple_configuration.html" target="_blank"><strong>关于Debian开发学习日记</strong></a><strong>一文中，曾给出针对C/C++编码使用的vim配置。</strong></p> <p><strong><font color="#800000">4. 函数声明与定义（Function Declarations and Definitions）</font></strong></p> <p>返回类型和函数名在同一行，合适的话，参数也放在同一行。</p> <p>函数看上去像这样：</p><pre>ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
  DoSomething();
  ...
}

</pre>
<p>如果同一行文本较多，容不下所有参数：</p><pre>ReturnType ClassName::ReallyLongFunctionName(Type par_name1,
                                             Type par_name2,
                                             Type par_name3) {
  DoSomething();
  ...
}

</pre>
<p>甚至连第一个参数都放不下：</p><pre>ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
    Type par_name1,  // 4 space indent
    Type par_name2,
    Type par_name3) {
  DoSomething();  // 2 space indent
  ...
}

</pre>
<p>注意以下几点：</p>
<p>1) 返回值总是和函数名在同一行；</p>
<p>2) <font color="#800000">左圆括号（open parenthesis）</font>总是和函数名在同一行；</p>
<p>3) 函数名和左圆括号间没有空格；</p>
<p>4) 圆括号与参数间没有空格；</p>
<p>5) <font color="#800000">左大括号（open curly brace）</font>总在最后一个参数同一行的末尾处；</p>
<p>6) <font color="#800000">右大括号（close curly brace）</font>总是单独位于函数最后一行；</p>
<p>7) <font color="#800000">右圆括号（close parenthesis）</font>和左大括号间总是有一个空格；</p>
<p>8) 函数声明和实现处的所有形参名称必须保持一致；</p>
<p>9) 所有形参应尽可能对齐；</p>
<p>10) 缺省缩进为2个空格；</p>
<p>11) 独立封装的参数保持4个空格的缩进。</p>
<p>如果函数为<code>const</code>的，关键字<code>const</code>应与最后一个参数位于同一行。</p><pre>// Everything in this function signature fits on a single line
ReturnType FunctionName(Type par) const {
  ...
}

// This function signature requires multiple lines, but
// the const keyword is on the line with the last parameter.
ReturnType ReallyLongFunctionName(Type par1,
                                  Type par2) const {
  ...
}

</pre>
<p>如果有些参数没有用到，在函数定义处将参数名注释起来：</p><pre>// Always have named parameters in interfaces.
class Shape {
 public:
  virtual void Rotate(double radians) = 0;
}

// Always have named parameters in the declaration.
class Circle : public Shape {
 public:
  virtual void Rotate(double radians);
}

// Comment out unused named parameters in definitions.
void Circle::Rotate(double /*radians*/) {}</pre><pre>// Bad - if someone wants to implement later, it's not clear what the
// variable means.
void Circle::Rotate(double) {}

</pre>
<p><strong>译者注：关于UNIX/Linux风格为什么要把左大括号置于行尾（.cc文件的函数实现处，左大括号位于行首），我的理解是代码看上去比较简约，想想行首除了函数体被一对大括号封在一起之外，只有右大括号的代码看上去确实也舒服；Windows风格将左大括号置于行首的优点是匹配情况一目了然。</strong></p>
<p><strong><font color="#800000">5. 函数调用（Function Calls）</font></strong></p>
<p>尽量放在同一行，否则，将实参封装在圆括号中。</p>
<p>函数调用遵循如下形式：</p><pre>bool retval = DoSomething(argument1, argument2, argument3);

</pre>
<p>如果同一行放不下，可断为多行，后面每一行都和第一个实参对齐，左圆括号后和右圆括号前不要留空格：</p><pre>bool retval = DoSomething(averyveryveryverylongargument1,
                          argument2, argument3);

</pre>
<p>如果函数参数比较多，可以出于可读性的考虑每行只放一个参数：</p><pre>bool retval = DoSomething(argument1,
                          argument2,
                          argument3,
                          argument4);

</pre>
<p>如果函数名太长，以至于超过行最大长度，可以将所有参数独立成行：</p><pre>if (...) {
  ...
  ...
  if (...) {
    DoSomethingThatRequiresALongFunctionName(
        very_long_argument1,  // 4 space indent
        argument2,
        argument3,
        argument4);
  }

</pre>
<p><strong><font color="#800000">6. 条件语句（Conditionals）</font></strong></p>
<p>更提倡不在圆括号中添加空格，关键字<code>else</code>另起一行。</p>
<p>对基本条件语句有两种可以接受的格式，一种在圆括号和条件之间有空格，一种没有。</p>
<p>最常见的是没有空格的格式，那种都可以，还是<strong>一致性</strong>为主。如果你是在修改一个文件，参考当前已有格式；如果是写新的代码，参考目录下或项目中其他文件的格式，还在徘徊的话，就不要加空格了。</p><pre>if (condition) {  // no spaces inside parentheses
  ...  // 2 space indent.
} else {  // The else goes on the same line as the closing brace.
  ...
}

</pre>
<p>如果你倾向于在圆括号内部加空格：</p><pre>if ( condition ) {  // spaces inside parentheses - rare
  ...  // 2 space indent.
} else {  // The else goes on the same line as the closing brace.
  ...
}

</pre>
<p>注意所有情况下<code>if</code>和左圆括号间有个空格，右圆括号和左大括号（如果使用的话）间也要有个空格：</p><pre>if(condition)     // Bad - space missing after IF.
if (condition){   // Bad - space missing before {.
if(condition){    // Doubly bad.</pre><pre>if (condition) {  // Good - proper space after IF and before {.

</pre>
<p>有些条件语句写在同一行以增强可读性，只有当语句简单并且没有使用<code>else子句</code>时使用：</p><pre>if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();

</pre>
<p>如果语句有<code>else</code>分支是不允许的：</p><pre>// Not allowed - IF statement on one line when there is an ELSE clause
if (x) DoThis();
else DoThat();

</pre>
<p>通常，单行语句不需要使用大括号，如果你喜欢也无可厚非，也有人要求<code>if</code>必须使用大括号：</p><pre>if (condition)
  DoSomething();  // 2 space indent.

if (condition) {
  DoSomething();  // 2 space indent.
}

</pre>
<p>但如果语句中哪一分支使用了大括号的话，其他部分也必须使用：</p><pre>// Not allowed - curly on IF but not ELSE
if (condition) {
  foo;
} else
  bar;

// Not allowed - curly on ELSE but not IF
if (condition)
  foo;
else {
  bar;
}</pre><pre>&nbsp;</pre><pre>// Curly braces around both IF and ELSE required because
// one of the clauses used braces.
if (condition) {
  foo;
} else {
  bar;
}

</pre>
<p><strong><font color="#800000">7. 循环和开关选择语句（Loops and Switch Statements）</font></strong></p>
<p><code>switch</code>语句可以使用大括号分块；空循环体应使用<code>{}</code>或<code>continue</code>。</p>
<p><code>switch</code>语句中的<code>case</code>块可以使用大括号也可以不用，取决于你的喜好，使用时要依下文所述。</p>
<p>如果有不满足<code>case</code>枚举条件的值，要总是包含一个<code>default</code>（如果有输入值没有<code>case</code>去处理，编译器将报警）。如果<code>default</code>永不会执行，可以简单的使用<code>assert</code>：</p><pre>switch (var) {
  case 0: {  // 2 space indent
    ...      // 4 space indent
    break;
  }
  case 1: {
    ...
    break;
  }
  default: {
    assert(false);
  }
}

</pre>
<p>空循环体应使用<code>{}</code>或<code>continue</code>，而不是一个简单的分号：</p><pre>while (condition) {
  // Repeat test until it returns false.
}
for (int i = 0; i &lt; kSomeNumber; ++i) {}  // Good - empty body.
while (condition) continue;  // Good - continue indicates no logic.</pre><pre>while (condition);  // Bad - looks like part of do/while loop.

</pre>
<p><strong><font color="#800000">8. 指针和引用表达式（Pointers and Reference Expressions）</font></strong></p>
<p>句点（<code>.</code>）或箭头（<code>-&gt;</code>）前后不要有空格，指针/地址操作符（<code>*、&amp;</code>）后不要有空格。</p>
<p>下面是指针和引用表达式的正确范例：</p><pre>x = *p;
p = &amp;x;
x = r.y;
x = r-&gt;y;

</pre>
<p>注意：</p>
<p>1) 在访问成员时，句点或箭头前后没有空格；</p>
<p>2) 指针操作符<code>*</code>或<code>&amp;</code>后没有空格。</p>
<p>在声明指针变量或参数时，星号与类型或变量名紧挨都可以：</p><pre>// These are fine, space preceding.
char *c;
const string &amp;str;

// These are fine, space following.
char* c;    // but remember to do "char* c, *d, *e, ...;"!
const string&amp; str;</pre><pre>char * c;  // Bad - spaces on both sides of *
const string &amp; str;  // Bad - spaces on both sides of &amp;

</pre>
<p>同一个文件（新建或现有）中起码要保持一致。</p>
<p><strong>译者注：个人比较习惯与变量紧挨的方式</strong><strong>。</strong></p>
<p><strong><font color="#800000">9. 布尔表达式（Boolean Expressions）</font></strong></p>
<p>如果一个布尔表达式超过标准行宽（80字符），如果断行要统一一下。</p>
<p>下例中，逻辑与（<code>&amp;&amp;</code>）操作符总位于行尾：</p><pre>if (this_one_thing &gt; this_other_thing &amp;&amp;
    a_third_thing == a_fourth_thing &amp;&amp;
    yet_another &amp; last_one) {
  ...
}

</pre>
<p>两个逻辑与（<code>&amp;&amp;</code>）操作符都位于行尾，可以考虑额外插入圆括号，合理使用的话对增强可读性是很有帮助的。</p>
<p><strong>译者注：个人比较习惯逻辑运算符位于行首，逻辑关系一目了然，各人喜好而已，至于加不加圆括号的问题，如果你对优先级了然于胸的话可以不加，但可读性总是差了些</strong><strong>。</strong></p>
<p><strong><font color="#800000">10. 函数返回值（Return Values）</font></strong></p>
<p><code>return</code>表达式中不要使用圆括号。</p>
<p>函数返回时不要使用圆括号：</p><pre>return x;  // not return(x);

</pre>
<p><strong><font color="#800000">11. 变量及数组初始化（Variable and Array Initialization）</font></strong></p>
<p>选择<code>=</code>还是<code>()</code>。</p>
<p>需要做二者之间做出选择，下面的形式都是正确的：</p><pre>int x = 3;
int x(3);
string name("Some Name");
string name = "Some Name";

</pre>
<p><strong><font color="#800000">12. 预处理指令（Preprocessor Directives）</font></strong></p>
<p>预处理指令不要缩进，从行首开始。</p>
<p>即使预处理指令位于缩进代码块中，指令也应从行首开始。</p><pre>// Good - directives at beginning of line
  if (lopsided_score) {
#if DISASTER_PENDING      // Correct -- Starts at beginning of line
    DropEverything();
#endif
    BackToNormal();
  }</pre><pre>// Bad - indented directives
  if (lopsided_score) {
    #if DISASTER_PENDING  // Wrong!  The "#if" should be at beginning of line
    DropEverything();
    #endif                // Wrong!  Do not indent "#endif"
    BackToNormal();
  }

</pre>
<p><strong><font color="#800000">13. 类格式（Class Format）</font></strong></p>
<p>声明属性依次序是<code>public:</code>、<code>protected:</code>、<code>private:</code>，每次缩进1个空格<font color="#800000">（译者注，为什么不是两个呢？也有人提倡<code>private</code>在前，对于声明了哪些数据成员一目了然，还有人提倡依逻辑关系将变量与操作放在一起，都有道理<code>:-)</code>）</font>。</p>
<p>类声明（对类注释不了解的话，参考<a title="[译]Google C++编程风格指南（六）" href="http://www.cppblog.com/Fox/archive/2008/07/22/56873.html" target="_blank">第六篇</a>中的<strong>类注释</strong>一节）的基本格式如下：</p><pre>class MyClass : public OtherClass {
 public:      // Note the 1 space indent!
  MyClass();  // Regular 2 space indent.
  explicit MyClass(int var);
  ~MyClass() {}

  void SomeFunction();
  void SomeFunctionThatDoesNothing() {
  }

  void set_some_var(int var) { some_var_ = var; }
  int some_var() const { return some_var_; }

 private:
  bool SomeInternalFunction();

  int some_var_;
  int some_other_var_;
  DISALLOW_COPY_AND_ASSIGN(MyClass);
};

</pre>
<p>注意：</p>
<p>1) 所以基类名应在80列限制下尽量与子类名放在同一行；</p>
<p>2) 关键词<code>public:、</code><code>protected:</code>、<code>private:</code>要缩进1个空格<font color="#800000">（译者注，MSVC多使用tab缩进，且这三个关键词没有缩进）</font>；</p>
<p>3) 除第一个关键词（一般是<code>public）外，其他关键词前空一行，如果类比较小的话也可以不空；</code></p>
<p>4) 这些关键词后不要空行；</p>
<p>5) <code>public</code>放在最前面，然后是<code>protected</code>和<code>private</code>；</p>
<p>6) 关于声明次序参考<a title="[译]Google C++编程风格指南（三）" href="http://www.cppblog.com/Fox/archive/2008/07/16/56324.html" target="_blank">第三篇</a><strong>声明次序</strong>一节。</p>
<p><strong><font color="#800000">14. 初始化列表（Initializer Lists）</font></strong></p>
<p>构造函数初始化列表放在同一行或按四格缩进并排几行。</p>
<p>两种可以接受的初始化列表格式：</p><pre>// When it all fits on one line:
MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {

</pre>
<p>或</p><pre>// When it requires multiple lines, indent 4 spaces, putting the colon on
// the first initializer line:
MyClass::MyClass(int var)
    : some_var_(var),             // 4 space indent
      some_other_var_(var + 1) {  // lined up
  ...
  DoSomething();
  ...
}

</pre>
<p><strong><font color="#800000">15. 命名空间格式化（Namespace Formatting）</font></strong></p>
<p>命名空间内容不缩进。</p>
<p>命名空间不添加额外缩进层次，例如：</p><pre>namespace {

void foo() {  // Correct.  No extra indentation within namespace.
  ...
}

}  // namespace

</pre>
<p>不要缩进：</p><pre>namespace {

  // Wrong.  Indented when it should not be.
  void foo() {
    ...
  }

}  // namespace

</pre>
<p><strong><font color="#800000">16. 水平留白（Horizontal Whitespace）</font></strong></p>
<p>水平留白的使用因地制宜。不要在行尾添加无谓的留白。</p>
<p><strong>普通</strong>：</p><pre>void f(bool b) {  // Open braces should always have a space before them.
  ...
int i = 0;  // Semicolons usually have no space before them.
int x[] = { 0 };  // Spaces inside braces for array initialization are
int x[] = {0};    // optional.  If you use them, put them on both sides!
// Spaces around the colon in inheritance and initializer lists.
class Foo : public Bar {
 public:
  // For inline function implementations, put spaces between the braces
  // and the implementation itself.
  Foo(int b) : Bar(), baz_(b) {}  // No spaces inside empty braces.
  void Reset() { baz_ = 0; }  // Spaces separating braces from implementation.
  ...

</pre>
<p>添加冗余的留白会给其他人编辑时造成额外负担，因此，不要加入多余的空格。如果确定一行代码已经修改完毕，将多余的空格去掉；或者在专门清理空格时去掉（确信没有其他人在使用）。</p>
<p><strong>循环和条件语句</strong>：</p><pre>if (b) {          // Space after the keyword in conditions and loops.
} else {          // Spaces around else.
}
while (test) {}   // There is usually no space inside parentheses.
switch (i) {
for (int i = 0; i &lt; 5; ++i) {
switch ( i ) {    // Loops and conditions may have spaces inside
if ( test ) {     // parentheses, but this is rare.  Be consistent.
for ( int i = 0; i &lt; 5; ++i ) {
for ( ; i &lt; 5 ; ++i) {  // For loops always have a space after the
  ...                   // semicolon, and may have a space before the
                        // semicolon.
switch (i) {
  case 1:         // No space before colon in a switch case.
    ...
  case 2: break;  // Use a space after a colon if there's code after it.

</pre>
<p><strong>操作符</strong>：</p><pre>x = 0;              // Assignment operators always have spaces around
                    // them.
x = -5;             // No spaces separating unary operators and their
++x;                // arguments.
if (x &amp;&amp; !y)
  ...
v = w * x + y / z;  // Binary operators usually have spaces around them,
v = w*x + y/z;      // but it's okay to remove spaces around factors.
v = w * (x + z);    // Parentheses should have no spaces inside them.

</pre>
<p><strong>模板和转换</strong>：</p><pre>vector&lt;string&gt; x;           // No spaces inside the angle
y = static_cast&lt;char*&gt;(x);  // brackets (&lt; and &gt;), before
                            // &lt;, or between &gt;( in a cast.
vector&lt;char *&gt; x;           // Spaces between type and pointer are
                            // okay, but be consistent.
set&lt;list&lt;string&gt; &gt; x;       // C++ requires a space in &gt; &gt;.
set&lt; list&lt;string&gt; &gt; x;      // You may optionally make use
                            // symmetric spacing in &lt; &lt;.

</pre>
<p><strong><font color="#800000">17. 垂直留白（Vertical Whitespace）</font></strong></p>
<p>垂直留白越少越好。</p>
<p>这不仅仅是规则而是原则问题了：不是非常有必要的话就不要使用空行。尤其是：不要在两个函数定义之间空超过2行，函数体头、尾不要有空行，函数体中也不要随意添加空行。</p>
<p>基本原则是：同一屏可以显示越多的代码，程序的控制流就越容易理解。当然，过于密集的代码块和过于疏松的代码块同样难看，取决于你的判断，但通常是越少越好。</p>
<p>函数头、尾不要有空行：</p><pre>void Function() {

  // Unnecessary blank lines before and after

}

</pre>
<p>代码块头、尾不要有空行：</p><pre>while (condition) {
  // Unnecessary blank line after

}
if (condition) {

  // Unnecessary blank line before
}

</pre>
<p><code>if-else</code>块之间空一行还可以接受：</p><pre>if (condition) {
  // Some lines of code too small to move to another function,
  // followed by a blank line.

} else {
  // Another block of code
}

</pre>
<p>______________________________________ </p>
<p>译者：首先说明，对于代码格式，因人、因系统各有优缺点，但同一个项目中遵循同一标准还是有必要的： 
<p><strong>1. 行宽原则上不超过80列，把22寸的显示屏都占完，怎么也说不过去；</strong></p>
<p><strong>2. 尽量不使用非ASCII字符，如果使用的话，参考UTF-8格式（尤其是UNIX/Linux下，Windows下可以考虑宽字符），尽量不将字符串常量耦合到代码中，比如独立出资源文件，这不仅仅是风格问题了；</strong></p>
<p><strong>3. UNIX/Linux下无条件使用空格，MSVC的话使用Tab也无可厚非；</strong></p>
<p><strong>4. 函数参数、逻辑条件、初始化列表：要么所有参数和函数名放在同一行，要么所有参数并排分行；</strong></p>
<p><strong>5. 除函数定义的左大括号可以置于行首外，包括函数/类/结构体/枚举声明、各种语句的左大括号置于行尾，所有右大括号独立成行；</strong></p>
<p><strong>6. ./-&gt;操作符前后不留空格，*/&amp;不要前后都留，一个就可，靠左靠右依各人喜好；</strong></p>
<p><strong>7. 预处理指令/命名空间不使用额外缩进，类/结构体/枚举/函数/语句使用缩进；</strong></p>
<p><strong>8. 初始化用=还是()依个人喜好，统一就好；</strong></p>
<p><strong>9. return不要加()；</strong></p>
<p><strong>10. 水平/垂直留白不要滥用，怎么易读怎么来。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/56922.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-23 11:43 <a href="http://www.cppblog.com/Fox/archive/2008/07/23/56922.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（六）</title><link>http://www.cppblog.com/Fox/archive/2008/07/22/56873.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 22 Jul 2008 09:02:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/22/56873.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56873.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/22/56873.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56873.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56873.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">注释</font></strong></li></ul> <p>注释虽然写起来很痛苦，但对保证代码可读性至为重要，下面的规则描述了应该注释什么、注释在哪儿。当然也要记住，注释的确很重要，但最好的代码<font color="#800000">本身就是文档（self-documenting）</font>，类型和变量命名意义明确要比通过注释解释模糊的命名好得多。</p> <p>注释是为别人（下一个需要理解你的代码的人）而写的，认真点吧，那下一个人可能就是你！</p> <p><strong><font color="#800000">1. 注释风格（Comment Style）</font></strong></p> <p>使用<code>//</code>或<code>/* */</code>，统一就好。</p> <p><code>//</code>或<code>/* */</code>都可以，<code>//</code>只是用的更加广泛，在如何注释和注释风格上确保统一。</p> <p></p> <p><strong><font color="#800000">2. 文件注释（File Comments）</font></strong>  <p>在每一个文件开头加入版权公告，然后是文件内容描述。</p> <p><strong>法律公告和作者信息</strong>：</p> <p>每一文件包含以下项，依次是：</p> <p>1) <font color="#800000">版权（copyright statement）</font>：如<code>Copyright 2008 Google Inc.</code>；</p> <p>2) <font color="#800000">许可版本（license boilerplate）</font>：为项目选择合适的许可证版本，如<a title="Apache License" href="http://en.wikipedia.org/wiki/Apache_2.0_licensing" target="_blank">Apache 2.0</a>、<a title="BSD licenses" href="http://en.wikipedia.org/wiki/BSD_license" target="_blank">BSD</a>、<a title="GNU Lesser General Public License" href="http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License" target="_blank">LGPL</a>、<a title="GNU General Public License" href="http://en.wikipedia.org/wiki/GNU_General_Public_License" target="_blank">GPL</a>；</p> <p>3) <font color="#800000">作者（author line）</font>：标识文件的原始作者。</p> <p>如果你对其他人创建的文件做了重大修改，将你的信息添加到作者信息里，这样当其他人对该文件有疑问时可以知道该联系谁。</p> <p><strong>文件内容</strong>：</p> <p>每一个文件版权许可及作者信息后，都要对文件内容进行注释说明。</p> <p>通常，<code>.h</code>文件要对所声明的类的功能和用法作简单说明，<code>.cc</code>文件包含了更多的实现细节或算法讨论，如果你感觉这些实现细节或算法讨论对于阅读有帮助，可以把<code>.cc</code>中的注释放到<code>.h</code>中，并在<code>.cc</code>中指出文档在<code>.h</code>中。</p> <p>不要单纯在<code>.h</code>和<code>.cc</code>间复制注释，复制的注释偏离了实际意义。</p> <p><strong><font color="#800000">3. 类注释（Class Comments）</font></strong></p> <p>每个类的定义要附着描述类的功能和用法的注释。</p><pre>// Iterates over the contents of a GargantuanTable.  Sample usage:
//    GargantuanTable_Iterator* iter = table-&gt;NewIterator();
//    for (iter-&gt;Seek("foo"); !iter-&gt;done(); iter-&gt;Next()) {
//      process(iter-&gt;key(), iter-&gt;value());
//    }
//    delete iter;
class GargantuanTable_Iterator {
  ...
};

</pre>
<p>如果你觉得已经在文件顶部详细描述了该类，想直接简单的来上一句“完整描述见文件顶部”的话，还是多少在类中加点注释吧。</p>
<p>如果类有任何<font color="#800000">同步前提（synchronization assumptions）</font>，文档说明之。如果该类的实例可被多线程访问，使用时务必注意文档说明。</p>
<p><strong><font color="#800000">4. 函数注释（Function Comments）</font></strong></p>
<p>函数声明处注释描述函数功能，定义处描述函数实现。</p>
<p><strong>函数声明</strong>：</p>
<p>注释于声明之前，描述函数功能及用法，注释使用描述式（"Opens the file"）而非指令式（"Open the file"）；注释只是为了描述函数而不是告诉函数做什么。通常，注释不会描述函数如何实现，那是定义部分的事情。</p>
<p>函数声明处注释的内容：</p>
<p>1) <font color="#800000">inputs（输入）</font>及<font color="#800000">outputs（输出）</font>；</p>
<p>2) 对类成员函数而言：函数调用期间对象是否需要保持引用参数，是否会释放这些参数；</p>
<p>3) 如果函数分配了空间，需要由调用者释放；</p>
<p>4) 参数是否可以为<code>NULL</code>；</p>
<p>5) 是否存在函数使用的<font color="#800000">性能隐忧（performance implications）</font>；</p>
<p>6) 如果函数是<font color="#800000">可重入的（re-entrant）</font>，其<font color="#800000">同步前提（synchronization assumptions）</font>是什么？</p>
<p>举例如下：</p><pre>// Returns an iterator for this table.  It is the client's
// responsibility to delete the iterator when it is done with it,
// and it must not use the iterator once the GargantuanTable object
// on which the iterator was created has been deleted.
//
// The iterator is initially positioned at the beginning of the table.
//
// This method is equivalent to:
//    Iterator* iter = table-&gt;NewIterator();
//    iter-&gt;Seek("");
//    return iter;
// If you are going to immediately seek to another place in the
// returned iterator, it will be faster to use NewIterator()
// and avoid the extra seek.
Iterator* GetIterator() const;

</pre>
<p>但不要有无谓冗余或显而易见的注释，下面的注释就没有必要加上“returns false otherwise”，因为已经暗含其中了：</p><pre>// Returns true if the table cannot hold any more entries.
bool IsTableFull();

</pre>
<p>注释构造/析构函数时，记住，读代码的人知道构造/析构函数是什么，所以“destroys this object”这样的注释是没有意义的。说明构造函数对参数做了什么（例如，是否是指针的所有者）以及析构函数清理了什么，如果都是无关紧要的内容，直接省掉注释，析构函数前没有注释是很正常的。</p>
<p><strong>函数定义</strong>：</p>
<p>每个函数定义时要以注释说明函数功能和实现要点，如使用的漂亮代码、实现的简要步骤、如此实现的理由、为什么前半部分要加锁而后半部分不需要。</p>
<p>不要从<code>.h</code>文件或其他地方的函数声明处直接复制注释，简要说明函数功能是可以的，但重点要放在如何实现上。</p>
<p><strong><font color="#800000">5. 变量注释（Variable Comments）</font></strong></p>
<p>通常变量名本身足以很好说明变量用途，特定情况下，需要额外注释说明。</p>
<p><strong>类数据成员</strong>：</p>
<p>每个类数据成员（也叫实例变量或成员变量）应注释说明用途，如果变量可以接受<code>NULL</code>或-1等<font color="#800000">警戒值（sentinel values）</font>，须说明之，如：</p><pre>private:
 // Keeps track of the total number of entries in the table.
 // Used to ensure we do not go over the limit. -1 means
 // that we don't yet know how many entries the table has.
 int num_total_entries_;

</pre>
<p><strong>全局变量（常量）</strong>：</p>
<p>和数据成员相似，所有全局变量（常量）也应注释说明含义及用途，如：</p><pre>// The total number of tests cases that we run through in this regression test.
const int kNumTestCases = 6;

</pre>
<p><strong><font color="#800000">6. 实现注释（Implementation Comments）</font></strong></p>
<p>对于实现代码中巧妙的、晦涩的、有趣的、重要的地方加以注释。</p>
<p><strong>代码前注释</strong>：</p>
<p>出彩的或复杂的代码块前要加注释，如：</p><pre>// Divide result by two, taking into account that x
// contains the carry from the add.
for (int i = 0; i &lt; result-&gt;size(); i++) {
  x = (x &lt;&lt; 8) + (*result)[i];
  (*result)[i] = x &gt;&gt; 1;
  x &amp;= 1;
}

</pre>
<p><strong>行注释</strong>：</p>
<p>比较隐晦的地方要在行尾加入注释，可以在代码之后空两格加行尾注释，如：</p><pre>// If we have enough memory, mmap the data portion too.
mmap_budget = max&lt;int64&gt;(0, mmap_budget - index_-&gt;length());
if (mmap_budget &gt;= data_size_ &amp;&amp; !MmapData(mmap_chunk_bytes, mlock))
  return;  // Error already logged.

</pre>
<p>注意，有两块注释描述这段代码，当函数返回时注释提及错误已经被记入日志。</p>
<p>前后相邻几行都有注释，可以适当调整使之可读性更好：</p><pre>...
DoSomething();                  // Comment here so the comments line up.
DoSomethingElseThatIsLonger();  // Comment here so there are two spaces between
                                // the code and the comment.
...

</pre>
<p><strong>NULL、true/false、1、2、3……</strong>：</p>
<p>向函数传入、布尔值或整数时，要注释说明含义，或使用常量让代码望文知意，比较一下：</p><pre>bool success = CalculateSomething(interesting_value,
                                  10,
                                  false,
                                  NULL);  // What are these arguments??

</pre>
<p>和：</p><pre>bool success = CalculateSomething(interesting_value,
                                  10,     // Default base value.
                                  false,  // Not the first time we're calling this.
                                  NULL);  // No callback.

</pre>
<p>使用常量或描述性变量：</p><pre>const int kDefaultBaseValue = 10;
const bool kFirstTimeCalling = false;
Callback *null_callback = NULL;
bool success = CalculateSomething(interesting_value,
                                  kDefaultBaseValue,
                                  kFirstTimeCalling,
                                  null_callback);

</pre>
<p><strong>不要</strong>：</p>
<p>注意永远不要用自然语言翻译代码作为注释，要假设读你代码的人C++比你强:D：</p><pre>// Now go through the b array and make sure that if i occurs,
// the next element is i+1.
...        // Geez.  What a useless comment.

</pre>
<p><strong><font color="#800000">7. 标点、拼写和语法（Punctuation, Spelling and Grammar）</font></strong></p>
<p>留意标点、拼写和语法，写的好的注释比差的要易读的多。</p>
<p>注释一般是包含适当大写和句点（.）的完整的句子，短一点的注释（如代码行尾的注释）可以随意点，依然要注意风格的一致性。完整的句子可读性更好，也可以说明该注释是完整的而不是一点不成熟的想法。</p>
<p>虽然被别人指出该用<font color="#800000">分号（semicolon）</font>的时候用了<font color="#800000">逗号（comma）</font>有点尴尬。清晰易读的代码还是很重要的，适当的标点、拼写和语法对此会有所帮助。</p>
<p><strong><font color="#800000">8. TODO注释（TODO Comments）</font></strong></p>
<p>对那些临时的、短期的解决方案，或已经够好但并不完美的代码使用<code>TODO</code>注释。</p>
<p>这样的注释要使用全大写的字符串<code>TODO</code>，后面<font color="#800000">括号（parentheses）</font>里加上你的大名、邮件地址等，还可以加上<font color="#800000">冒号（colon）</font>：目的是可以根据统一的<code>TODO</code>格式进行查找：</p><pre>// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.

</pre>
<p>如果加上是为了在“将来某一天做某事”，可以加上一个特定的时间（"Fix by November 2005"）或事件（"Remove this code when all clients can handle XML responses."）。</p>
<p>______________________________________ </p>
<p>译者：注释也是比较人性化的约定了： 
<p><strong>1. 关于注释风格，很多C++的coders更喜欢行注释，C coders或许对块注释依然情有独钟，或者在文件头大段大段的注释时使用块注释；</strong></p>
<p><strong>2. </strong><strong>文件注释可以炫耀你的成就，也是为了捅了篓子别人可以找你；</strong></p>
<p><strong>3. 注释要言简意赅，不要拖沓冗余，复杂的东西简单化和简单的东西复杂化都是要被鄙视的；</strong></p>
<p><strong>4. 对于Chinese coders来说，用英文注释还是用中文注释，it is a problem，但不管怎样，注释是为了让别人看懂，难道是为了炫耀编程语言之外的你的母语或外语水平吗；</strong></p>
<p><strong>5. 注释不要太乱，适当的缩进才会让人乐意看，但也没有必要规定注释从第几列开始（我自己写代码的时候总喜欢这样），UNIX/LINUX下还可以约定是使用tab还是space，个人倾向于space；</strong></p>
<p><strong>6. TODO很不错，有时候，注释确实是为了标记一些未完成的或完成的不尽如人意的地方，这样一搜索，就知道还有哪些活要干，日志都省了。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/56873.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-22 17:02 <a href="http://www.cppblog.com/Fox/archive/2008/07/22/56873.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（五）</title><link>http://www.cppblog.com/Fox/archive/2008/07/22/56845.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 22 Jul 2008 03:59:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/22/56845.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56845.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/22/56845.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56845.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56845.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">命名约定</font></strong></li></ul> <p>最重要的一致性规则是命名管理，命名风格直接可以直接确定命名实体是：类型、变量、函数、常量、宏等等，无需查找实体声明，我们大脑中的模式匹配引擎依赖于这些命名规则。</p> <p>命名规则具有一定随意性，但相比按个人喜好命名，一致性更重要，所以不管你怎么想，规则总归是规则。</p> <p><strong><font color="#800000">1. 通用命名规则（General Naming Rules）</font></strong></p> <p>函数命名、变量命名、文件命名应具有描述性，不要过度缩写，类型和变量应该是名词，函数名可以用“命令性”动词。</p> <p><strong>如何命名</strong>：  <p>尽可能给出描述性名称，不要节约空间，让别人很快理解你的代码更重要，好的命名选择：<pre>int num_errors;                  // Good.
int num_completed_connections;   // Good.</pre>
<p></p>
<p>丑陋的命名使用模糊的缩写或随意的字符：<pre>int n;                           // Bad - meaningless.
int nerr;                        // Bad - ambiguous abbreviation.
int n_comp_conns;                // Bad - ambiguous abbreviation.</pre>
<p></p>
<p>类型和变量名一般为名词：如<code>FileOpener</code>、<code>num_errors</code>。 
<p>函数名通常是指令性的，如<code>OpenFile()</code>、<code>set_num_errors()</code>，访问函数需要描述的更细致，要与其访问的变量相吻合。 
<p><strong>缩写</strong>： 
<p>除非放到项目外也非常明了，否则不要使用缩写，例如：<pre>// Good
// These show proper names with no abbreviations.
int num_dns_connections;  // Most people know what "DNS" stands for.
int price_count_reader;   // OK, price count. Makes sense.</pre><pre>&nbsp;</pre><pre>// Bad!
// Abbreviations can be confusing or ambiguous outside a small group.
int wgc_connections;  // Only your group knows what this stands for.
int pc_reader;        // Lots of things can be abbreviated "pc".</pre>
<p></p>
<p>不要用省略字母的缩写：<pre>int error_count;  // Good.</pre><pre>int error_cnt;    // Bad.</pre>
<p></p>
<p><strong><font color="#800000">2. 文件命名（File Names）</font></strong> 
<p>文件名要全部小写，可以包含下划线（_）或短线（-），按项目约定来。</p>
<p>可接受的文件命名：</p>
<p><code>my_useful_class.cc<br>my-useful-class.cc<br>myusefulclass.cc</code></p>
<p>C++文件以<code>.cc</code>结尾，头文件以<code>.h</code>结尾。</p>
<p>不要使用已经存在于<code>/usr/include</code>下的文件名<font color="#800000">（译者注，对UNIX、Linux等系统而言）</font>，如<code>db.h</code>。</p>
<p>通常，尽量让文件名更加明确，<code>http_server_logs.h</code>就比<code>logs.h</code>要好，定义类时文件名一般成对出现，如<code>foo_bar.h</code>和<code>foo_bar.cc</code>，对应类<code>FooBar</code>。</p>
<p>内联函数必须放在<code>.h</code>文件中，如果内联函数比较短，就直接放在<code>.h</code>中。如果代码比较长，可以放到以<code>-inl.h</code>结尾的文件中。对于包含大量内联代码的类，可以有三个文件：</p><pre>url_table.h      // The class declaration.
url_table.cc     // The class definition.
url_table-inl.h  // Inline functions that include lots of code.</pre>
<p></p>
<p>参考<a title="[译]Google C++编程风格指南（一）" href="http://www.cppblog.com/Fox/archive/2008/07/10/55818.html" target="_blank">第一篇</a><strong>-inl.h文件</strong>一节。</p>
<p><strong><font color="#800000">3. 类型命名（Type Names）</font></strong></p>
<p>类型命名每个单词以大写字母开头，不包含下划线：<code>MyExcitingClass</code>、<code>MyExcitingEnum</code>。</p>
<p>所有类型命名——类、结构体、类型定义（typedef）、枚举——使用相同约定，例如：</p>
<p><pre>// classes and structs
class UrlTable { ... 
class UrlTableTester { ... 
struct UrlTableProperties { ...

// typedefs
typedef hash_map&lt;UrlTableProperties *, string&gt; PropertiesMap;

// enums
enum UrlTableErrors { ...</pre>
<p></p>
<p><strong><font color="#800000">4. 变量命名（Variable Names）</font></strong></p>
<p>变量名一律小写，单词间以下划线相连，类的成员变量以下划线结尾，如<code>my_exciting_local_variable</code>、<code>my_exciting_member_variable_</code>。</p>
<p><strong>普通变量命名</strong>：</p>
<p>举例：</p><pre>string table_name;  // OK - uses underscore.
string tablename;   // OK - all lowercase.</pre><pre>string tableName;   // Bad - mixed case.</pre>
<p></p>
<p></p>
<p><strong>类数据成员</strong>：</p>
<p>结构体的数据成员可以和普通变量一样，不用像类那样接下划线：</p><pre>struct UrlTableProperties {
  string name;
  int num_entries;
}</pre>
<p></p>
<p>结构体与类的讨论参考<a title="[译]Google C++编程风格指南（三）" href="http://www.cppblog.com/Fox/archive/2008/07/16/56324.html" target="_blank">第三篇</a><strong>结构体vs.类</strong>一节。</p>
<p><strong>全局变量</strong>：</p>
<p>对全局变量没有特别要求，少用就好，可以以<code>g_</code>或其他易与局部变量区分的标志为前缀。</p>
<p><strong><font color="#800000">5. 常量命名（Constant Names）</font></strong></p>
<p>在名称前加<code>k</code>：<code>kDaysInAWeek</code>。</p>
<p>所有编译时常量（无论是局部的、全局的还是类中的）和其他变量保持些许区别，<code>k</code>后接大写字母开头的单词：</p><pre>const int kDaysInAWeek = 7;</pre>
<p></p>
<p><strong><font color="#800000">6. 函数命名（Function Names）</font></strong></p>
<p><font color="#800000">普通函数（regular functions，译者注，这里与访问函数等特殊函数相对）</font>大小写混合，<font color="#800000">存取函数（accessors and mutators）</font>则要求与变量名匹配：<code>MyExcitingFunction()</code>、<code>MyExcitingMethod()</code>、<code>my_exciting_member_variable()</code>、<code>set_my_exciting_member_variable()</code>。</p>
<p><strong>普通函数</strong>：</p>
<p>函数名以大写字母开头，每个单词首字母大写，没有下划线：</p><pre>AddTableEntry()
DeleteUrl()</pre>
<p></p>
<p><strong>存取函数</strong>：</p>
<p>存取函数要与存取的变量名匹配，这儿摘录一个拥有实例变量<code>num_entries_</code>的类：</p><pre>class MyClass {
 public:
  ...
  int num_entries() const { return num_entries_; }
  void set_num_entries(int num_entries) { num_entries_ = num_entries; }

 private:
  int num_entries_;
};</pre>
<p></p>
<p>其他短小的内联函数名也可以使用小写字母，例如，在循环中调用这样的函数甚至都不需要缓存其值，小写命名就可以接受。</p>
<p><strong>译者注：从这一点上可以看出，小写的函数名意味着可以直接内联使用。</strong></p>
<p><strong><font color="#800000">7. 命名空间（Namespace Names）</font></strong></p>
<p>命名空间的名称是全小写的，其命名基于项目名称和目录结构：<code>google_awesome_project</code>。</p>
<p>关于命名空间的讨论和如何命名，参考<a title="[译]Google C++编程风格指南（二）" href="http://www.cppblog.com/Fox/archive/2008/07/14/56109.html" target="_blank">第二篇</a><strong>命名空间</strong>。</p>
<p><strong><font color="#800000">8. 枚举命名（Enumerator Names）</font></strong></p>
<p>枚举值应全部大写，单词间以下划线相连：<code>MY_EXCITING_ENUM_VALUE</code>。</p>
<p>枚举名称属于类型，因此大小写混合：<code>UrlTableErrors</code>。</p><pre>enum UrlTableErrors {
  OK = 0,
  ERROR_OUT_OF_MEMORY,
  ERROR_MALFORMED_INPUT,
};</pre>
<p></p>
<p><strong><font color="#800000">9. 宏命名（Macro Names）</font></strong></p>
<p>你并不打算使用宏，对吧？如果使用，像这样：<code>MY_MACRO_THAT_SCARES_SMALL_CHILDREN</code>。</p>
<p>参考<a title="[译]Google C++编程风格指南（四）" href="http://www.cppblog.com/Fox/archive/2008/07/21/56760.html" target="_blank">第四篇</a><strong>预处理宏</strong>，通常是不使用宏的，如果绝对要用，其命名像枚举命名一样全部大写、使用下划线：</p>
<p><pre>#define ROUND(x) ...
#define PI_ROUNDED 3.0
MY_EXCITING_ENUM_VALUE</pre>
<p></p>
<p><strong><font color="#800000">10. 命名规则例外（Exceptions to Naming Rules）</font></strong></p>
<p>当命名与现有C/C++实体相似的对象时，可参考现有命名约定： 
<dl>
<dt><code>bigopen()</code> 
<dd>函数名，参考<code>open()</code> 
<dt><code>uint</code> 
<dd><code>typedef类型定义</code> 
<dt><code>bigpos</code> 
<dd><code>struct</code>或<code>class，参考</code><code>pos</code> 
<dt><code>sparse_hash_map</code> 
<dd>STL相似实体；参考STL命名约定 
<dt><code>LONGLONG_MAX</code> 
<dd>常量，类似<code>INT_MAX</code></dd></dl>
<p>______________________________________ </p>
<p>译者：命名约定就相对轻松许多，在遵从代码一致性、可读性的前提下，略显随意： 
<p><strong>1. 总体规则：不要随意缩写，如果说ChangeLocalValue写作ChgLocVal还有情可原的话，把ModifyPlayerName写作MdfPlyNm就太过分了，除函数名可适当为动词外，其他命名尽量使用清晰易懂的名词；</strong></p>
<p><strong>2. </strong><strong>宏、枚举等使用全部大写+下划线；</strong></p>
<p><strong>3. 变量（含类、结构体成员变量）、文件、命名空间、存取函数等使用全部小写+下划线，类成员变量以下划线结尾，全局变量以g_开头；</strong></p>
<p><strong>4. 普通函数、类型（含类与结构体、枚举类型）、常量等使用大小写混合，不含下划线；</strong></p>
<p><strong>5. 参考现有或相近命名约定。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/56845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-22 11:59 <a href="http://www.cppblog.com/Fox/archive/2008/07/22/56845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（四）</title><link>http://www.cppblog.com/Fox/archive/2008/07/21/56760.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 21 Jul 2008 06:55:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/21/56760.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56760.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/21/56760.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56760.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56760.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">Google特有的风情</font></strong></li></ul> <p>Google有很多自己实现的使C++代码更加健壮的技巧、功能，以及有异于别处的C++的使用方式。</p> <p><strong><font color="#800000">1. 智能指针（Smart Pointers）</font></strong></p> <p>如果确实需要使用智能指针的话，scoped_ptr完全可以胜任。在非常特殊的情况下，例如对STL容器中对象，你应该只使用std::tr1::shared_ptr，任何情况下都不要使用auto_ptr。</p> <p>“智能”指针看上去是指针，其实是附加了语义的对象。以scoped_ptr为例，scoped_ptr被销毁时，删除了它所指向的对象。shared_ptr也是如此，而且，shared_ptr实现了<font color="#800000">引用计数（reference-counting）</font>，从而只有当它所指向的最后一个对象被销毁时，指针才会被删除。</p> <p>一般来说，我们倾向于设计对象隶属明确的代码，最明确的对象隶属是根本不使用指针，直接将对象作为一个<font color="#800000">域（field）</font>或局部变量使用。另一种极端是引用计数指针不属于任何对象，这样设计的问题是容易导致循环引用或其他导致对象无法删除的诡异条件，而且在每一次拷贝或赋值时连原子操作都会很慢。</p> <p>虽然不推荐这么做，但有些时候，引用计数指针是最简单有效的解决方案。  <p><strong>译者注：看来，Google所谓的不同之处，在于尽量避免使用智能指针:D，使用时也尽量局部化，并且，安全第一。</strong>  <ul> <li><strong><font color="#800000">其他C++特性</font></strong></li></ul> <p><strong><font color="#800000">1. 引用参数（Reference Arguments）</font></strong></p> <p>所以按引用传递的参数必须加上const。</p> <p><strong>定义：</strong>在C语言中，如果函数需要修改变量的值，<font color="#800000">形参（parameter）</font>必须为指针，如int foo(int *pval)。在C++中，函数还可以声明引用形参：int foo(int &amp;val)。</p> <p><strong>优点：</strong>定义形参为引用避免了像(*pval)++这样丑陋的代码，像拷贝构造函数这样的应用也是必需的，而且不像指针那样不接受空指针NULL。</p> <p><strong>缺点：</strong>容易引起误解，因为引用在语法上是值却拥有指针的语义。</p> <p><strong>结论：</strong></p> <p>函数形参表中，所有引用必须是const：</p><code> <p>void Foo(const string &amp;in, string *out);</p></code> <p>事实上这是一个硬性约定：输入参数为值或常数引用，输出参数为指针；输入参数可以是常数指针，但不能使用非常数引用形参。</p> <p>在强调参数不是拷贝而来，在对象生命期内必须一直存在时可以使用常数指针，最好将这些在注释中详细说明。bind2nd和mem_fun等STL适配器不接受引用形参，这种情况下也必须以指针形参声明函数。</p> <p><strong><font color="#800000">2. 函数重载（Function Overloading）</font></strong></p> <p>仅在输入参数类型不同、功能相同时使用重载函数（含构造函数），不要使用函数重载模仿缺省函数参数。</p> <p><strong>定义：</strong>可以定义一个函数参数类型为const string&amp;，并定义其重载函数类型为const char*。</p><code> <p>class MyClass {<br>public:<br>&nbsp; void Analyze(const string &amp;text);<br>&nbsp; void Analyze(const char *text, size_t textlen);<br>};</p></code> <p><strong>优点：</strong>通过重载不同参数的同名函数，令代码更加直观，模板化代码需要重载，同时为访问者带来便利。</p> <p><strong>缺点：</strong>限制使用重载的一个原因是在特定调用处很难确定到底调用的是哪个函数，另一个原因是当派生类只重载函数的部分变量会令很多人对继承语义产生困惑。此外在阅读库的客户端代码时，因缺省函数参数造成不必要的费解。</p> <p><strong>结论：</strong>如果你想重载一个函数，考虑让函数名包含参数信息，例如，使用AppendString()、AppendInt()而不是Append()。</p> <p><strong><font color="#800000">3. 缺省参数（Default Arguments）</font></strong></p> <p>禁止使用缺省函数参数。</p> <p><strong>优点：</strong>经常用到一个函数带有大量缺省值，偶尔会重写一下这些值，缺省参数为很少涉及的例外情况提供了少定义一些函数的方便。  <p><strong>缺点：</strong>大家经常会通过查看现有代码确定如何使用API，缺省参数使得复制粘贴以前的代码难以呈现所有参数，当缺省参数不适用于新代码时可能导致重大问题。  <p><strong>结论：</strong>所有参数必须明确指定，强制程序员考虑API和传入的各参数值，避免使用可能不为程序员所知的缺省参数。  <p><strong><font color="#800000">4. 变长数组和alloca（Variable-Length Arrays and alloca()）</font></strong></p> <p>禁止使用变长数组和alloca()。</p> <p><strong>优点：</strong>变长数组具有浑然天成的语法，变长数组和alloca()也都很高效。</p> <p><strong>缺点：</strong>变长数组和alloca()不是标准C++的组成部分，更重要的是，它们在<font color="#800000">堆栈（stack）</font>上根据数据分配大小可能导致难以发现的内存泄漏：“在我的机器上运行的好好的，到了产品中却莫名其妙的挂掉了”。</p> <p><strong>结论：</strong></p> <p>使用安全的<font color="#800000">分配器（allocator）</font>，如scoped_ptr/scoped_array。  <p><strong><font color="#800000">5. 友元（Friends）</font></strong></p> <p>允许合理使用友元类及友元函数。</p> <p>通常将友元定义在同一文件下，避免读者跑到其他文件中查找其对某个类私有成员的使用。经常用到友元的一个地方是将FooBuilder声明为Foo的友元，FooBuilder以便可以正确构造Foo的内部状态，而无需将该状态暴露出来。某些情况下，将一个单元测试用类声明为待测类的友元会很方便。</p> <p>友元延伸了（但没有打破）类的封装界线，当你希望只允许另一个类访问某个成员时，使用友元通常比将其声明为public要好得多。当然，大多数类应该只提供公共成员与其交互。  <p><strong><font color="#800000">6. 异常（Exceptions</font></strong><strong><font color="#800000">）</font></strong></p> <p>不要使用C++异常。</p> <p><strong>优点：</strong></p> <p>1) 异常允许上层应用决定如何处理在底层嵌套函数中发生的“不可能发生”的失败，不像出错代码的记录那么模糊费解；</p> <p>2) 应用于其他很多现代语言中，引入异常使得C++与Python、Java及其他与C++相近的语言更加兼容；</p> <p>3) 许多C++第三方库使用异常，关闭异常将导致难以与之结合；</p> <p>4) 异常是解决构造函数失败的唯一方案，虽然可以通过<font color="#800000">工厂函数（factory function）</font>或Init()方法模拟异常，但他们分别需要堆分配或新的“非法”状态；</p> <p>5) 在<font color="#800000">测试框架（testing framework）</font>中，异常确实很好用。</p> <p><strong>缺点：</strong></p> <p>1) 在现有函数中添加throw语句时，必须检查所有调用处，即使它们至少具有基本的异常安全保护，或者程序正常结束，永远不可能捕获该异常。例如：if <code>f()</code> calls <code>g()</code> calls <code>h()</code>，<code>h</code>抛出被<code>f</code>捕获的异常，<code>g</code>就要当心了，避免没有完全清理；</p> <p>2) 通俗一点说，异常会导致程序<font color="#800000">控制流（control flow）</font>通过查看代码无法确定：函数有可能在不确定的地方返回，从而导致代码管理和调试困难，当然，你可以通过规定何时何地如何使用异常来最小化的降低开销，却给开发人员带来掌握这些规定的负担；</p> <p>3) 异常安全需要RAII和不同编码实践。轻松、正确编写异常安全代码需要大量支撑。允许使用异常；</p> <p>4) 加入异常使二进制执行代码体积变大，增加了编译时长（或许影响不大），还可能增加地址空间压力；</p> <p>5) 异常的实用性可能会刺激开发人员在不恰当的时候抛出异常，或者在不安全的地方从异常中恢复，例如，非法用户输入可能导致抛出异常。如果允许使用异常会使得这样一篇编程风格指南长出很多（<font color="#800000">译者注，这个理由有点牵强:-(</font>）！</p> <p><strong>结论：</strong></p> <p>从表面上看，使用异常利大于弊，尤其是在新项目中，然而，对于现有代码，引入异常会牵连到所有依赖代码。如果允许异常在新项目中使用，在跟以前没有使用异常的代码整合时也是一个麻烦。因为Google现有的大多数C++代码都没有异常处理，引入带有异常处理的新代码相当困难。  <p>鉴于Google现有代码不接受异常，在现有代码中使用异常比在新项目中使用的代价多少要大一点，迁移过程会比较慢，也容易出错。我们也不相信异常的有效替代方案，如错误代码、断言等，都是严重负担。  <p>我们并不是基于哲学或道德层面反对使用异常，而是在实践的基础上。因为我们希望使用Google上的开源项目，但项目中使用异常会为此带来不便，因为我们也建议不要在Google上的开源项目中使用异常，如果我们需要把这些项目推倒重来显然不太现实。  <p>对于Windows代码来说，这一点有个例外（等到最后一篇吧:D）。  <p><strong>译者注：对于异常处理，显然不是短短几句话能够说清楚的，以构造函数为例，很多C++书籍上都提到当构造失败时只有异常可以处理，Google禁止使用异常这一点，仅仅是为了自身的方便，说大了，无非是基于软件管理成本上，实际使用中还是自己决定。</strong>  <p><strong><font color="#800000">7. 运行时类型识别（Run-Time Type Information, RTTI</font></strong><strong><font color="#800000">）</font></strong></p> <p>我们禁止使用RTTI。</p> <p><strong>定义：</strong>RTTI允许程序员在运行时识别C++类对象的类型。</p> <p><strong>优点：</strong></p> <p>RTTI在某些单元测试中非常有用，如在进行工厂类测试时用于检验一个新建对象是否为期望的动态类型。</p> <p>除测试外，极少用到。</p> <p><strong>缺点：</strong>运行时识别类型意味著设计本身有问题，如果你需要在运行期间确定一个对象的类型，这通常说明你需要重新考虑你的类的设计。</p> <p><strong>结论：</strong></p> <p>除单元测试外，不要使用RTTI，如果你发现需要所写代码因对象类型不同而动作各异的话，考虑换一种方式识别对象类型。</p> <p>虚函数可以实现随子类类型不同而执行不同代码，工作都是交给对象本身去完成。</p> <p>如果工作在对象之外的代码中完成，考虑双重分发方案，如Visitor模式，可以方便的在对象本身之外确定类的类型。</p> <p>如果你认为上面的方法你掌握不了，可以使用RTTI，但务必请三思，不要去手工实现一个<font color="#800000">貌似RTTI的方案（RTTI-like workaround）</font>，我们反对使用RTTI，同样反对贴上类型标签的貌似类继承的替代方案（<font color="#800000">译者注，使用就使用吧，不使用也不要造轮子:D</font>）。</p> <p><font color="#800000"><strong>8. 类型转换（Casting</strong></font><font color="#800000"><strong>）</strong></font></p> <p>使用<code>static_cast&lt;&gt;()</code>等C++的类型转换，不要使用<code>int y = (int)x</code>或<code>int y = int(x);</code>。</p> <p><strong>定义：</strong>C++引入了有别于C的不同类型的类型转换操作。</p> <p><strong>优点：</strong>C语言的类型转换问题在于操作比较含糊：有时是在做强制转换（如<code>(int)3.5</code>），有时是在做类型转换（如<code>(int)"hello"</code>）。另外，C++的类型转换查找更容易、更醒目。</p> <p><strong>缺点：</strong>语法比较<font color="#800000">恶心（nasty）</font>。</p> <p><strong>结论：</strong>使用C++风格而不要使用C风格类型转换。</p> <p>1) static_cast：和C风格转换相似可做值的强制转换，或指针的父类到子类的明确的向上转换；</p> <p>2) const_cast：移除const属性；</p> <p>3) reinterpret_cast：指针类型和整型或其他指针间不安全的相互转换，仅在你对所做一切了然于心时使用；</p> <p>4) dynamic_cast：除测试外不要使用，除单元测试外，如果你需要在运行时确定类型信息，说明设计有缺陷（参考<strong>RTTI</strong>）。</p> <p><strong><font color="#800000">9. 流（Streams</font></strong><strong><font color="#800000">）</font></strong></p> <p>只在记录日志时使用流。</p> <p><strong>定义：</strong>流是<code>printf()</code>和<code>scanf()</code>的替代。</p> <p><strong>优点：</strong>有了流，在输出时不需要关心对象的类型，不用担心格式化字符串与参数列表不匹配（虽然在gcc中使用printf也不存在这个问题），打开、关闭对应文件时，流可以自动构造、析构。</p> <p><strong>缺点：</strong>流使得<code>pread()</code>等功能函数很难执行，如果不使用<code>printf</code>之类的函数而是使用流很难对格式进行操作（尤其是常用的格式字符串<code>%.*s</code>），流不支持字符串操作符重新定序（<code>%1s</code>），而这一点对国际化很有用。</p> <p><strong>结论：</strong></p> <p>不要使用流，除非是日志接口需要，使用<code>printf</code>之类的代替。</p> <p>使用流还有很多利弊，代码一致性胜过一切，不要在代码中使用流。</p> <p><strong>拓展讨论：</strong></p> <p>对这一条规则存在一些争论，这儿给出深层次原因。回忆<font color="#800000">唯一性原则（Only One Way）</font>：我们希望在任何时候都只使用一种确定的I/O类型，使代码在所有I/O处保持一致。因此，我们不希望用户来决定是使用流还是<code>printf + read/write</code>，我们应该决定到底用哪一种方式。把日志作为例外是因为流非常适合这么做，也有一定的历史原因。</p> <p>流的支持者们主张流是不二之选，但观点并不是那么清晰有力，他们所指出流的所有优势也正是其劣势所在。流最大的优势是在输出时不需要关心输出对象的类型，这是一个亮点，也是一个不足：很容易用错类型，而编译器不会报警。使用流时容易造成的一类错误是：</p><pre>cout &lt;&lt; this;  // Prints the address
cout &lt;&lt; *this;  // Prints the contents</pre>
<p></p>
<p>编译器不会报错，因为&lt;&lt;被重载，就因为这一点我们反对使用操作符重载。</p>
<p>有人说<code>printf</code>的格式化丑陋不堪、易读性差，但流也好不到哪儿去。看看下面两段代码吧，哪个更加易读？</p><pre>cerr &lt;&lt; "Error connecting to '" &lt;&lt; foo-&gt;bar()-&gt;hostname.first
     &lt;&lt; ":" &lt;&lt; foo-&gt;bar()-&gt;hostname.second &lt;&lt; ": " &lt;&lt; strerror(errno);

fprintf(stderr, "Error connecting to '%s:%u: %s",
        foo-&gt;bar()-&gt;hostname.first, foo-&gt;bar()-&gt;hostname.second,
        strerror(errno));</pre>
<p></p>
<p>你可能会说，“把流封装一下就会比较好了”，这儿可以，其他地方呢？而且不要忘了，我们的目标是使语言尽可能小，而不是添加一些别人需要学习的新的内容。</p>
<p>每一种方式都是各有利弊，“没有最好，只有更好”，简单化的教条告诫我们必须从中选择其一，最后的多数决定是<code>printf + read/write</code>。</p>
<p><font color="#800000"><strong>10. 前置自增和自减（Preincrement and Predecrement</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>对于迭代器和其他模板对象使用前缀形式（<code>++i</code>）的自增、自减运算符。</p>
<p><strong>定义</strong>：对于变量在自增（<code>++i</code>或<code>i++</code>）或自减（<code>--i</code>或<code>i--</code>）后表达式的值又没有没用到的情况下，需要确定到底是使用前置还是后置的自增自减。</p>
<p><strong>优点</strong>：不考虑返回值的话，前置自增（<code>++i</code>）通常要比后置自增（<code>i++</code>）效率更高，因为后置的自增自减需要对表达式的值<code>i</code>进行一次拷贝，如果<code>i</code>是迭代器或其他非数值类型，拷贝的代价是比较大的。既然两种自增方式动作一样（<font color="#800000">译者注，不考虑表达式的值，相信你知道我在说什么</font>），为什么不直接使用前置自增呢？</p>
<p><strong>缺点</strong>：C语言中，当表达式的值没有使用时，传统的做法是使用后置自增，特别是在<code>for</code>循环中，有些人觉得后置自增更加易懂，因为这很像自然语言，主语（<code>i</code>）在谓语动词（<code>++</code>）前。</p>
<p><strong>结论</strong>：对简单数值（非对象）来说，两种都无所谓，对迭代器和模板类型来说，要使用前置自增（自减）。</p>
<p><strong><font color="#800000">11. const的使用（Use of const</font></strong><strong><font color="#800000">）</font></strong></p>
<p>我们强烈建议你在任何可以使用的情况下都要使用<code>const</code>。</p>
<p><strong>定义</strong>：在声明的变量或参数前加上关键字<code>const</code>用于指明变量值不可修改（如<code>const int foo</code>），为类中的函数加上<code>const</code>限定表明该函数不会修改类成员变量的状态（如<code>class Foo { int Bar(char c) const; };</code>）。</p>
<p><strong>优点</strong>：人们更容易理解变量是如何使用的，编辑器可以更好地进行类型检测、更好地生成代码。人们对编写正确的代码更加自信，因为他们知道所调用的函数被限定了能或不能修改变量值。即使是在无锁的多线程编程中，人们也知道什么样的函数是安全的。</p>
<p><strong>缺点</strong>：如果你向一个函数传入<code>const</code>变量，函数原型中也必须是<code>const</code>的（否则变量需要<code>const_cast</code>类型转换），在调用库函数时这尤其是个麻烦。</p>
<p><strong>结论</strong>：<code>const</code>变量、数据成员、函数和参数为编译时类型检测增加了一层保障，更好的尽早发现错误。因此，我们强烈建议在任何可以使用的情况下使用<code>const</code>：</p>
<p>1) 如果函数不会修改传入的引用或指针类型的参数，这样的参数应该为<code>const</code>；</p>
<p>2) 尽可能将函数声明为<code>const</code>，访问函数应该总是<code>const</code>，其他函数如果不会修改任何数据成员也应该是<code>const</code>，不要调用非<code>const</code>函数，不要返回对数据成员的非<code>const</code>指针或引用；</p>
<p>3) 如果数据成员在对象构造之后不再改变，可将其定义为<code>const</code>。</p>
<p>然而，也<strong>不要对<code>const</code>过度使用</strong>，像<code>const int * const * const x;</code>就有些过了，即便这样写精确描述了<code>x</code>，其实写成<code>const int** x</code>就可以了。</p>
<p>关键字<code>mutable</code>可以使用，但是在多线程中是不安全的，使用时首先要考虑线程安全。</p>
<p><strong><code>const</code>位置</strong>：</p>
<p>有人喜欢<code>int const *foo</code>形式不喜欢<code>const int* foo</code>，他们认为前者更加一致因此可读性更好：遵循了<code>const</code>总位于其描述的对象（<code>int</code>）之后的原则。但是，一致性原则不适用于此，“不要过度使用”的权威抵消了一致性使用。将<code>const</code>放在前面才更易读，因为在自然语言中形容词（<code>const</code>）是在名词（<code>int</code>）之前的。</p>
<p>这是说，我们提倡<code>const</code>在前，并不是要求，但要兼顾代码的一致性！</p>
<p><font color="#800000"><strong>12. 整型（Integer Types</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>C++内建整型中，唯一用到的是<code>int</code>，如果程序中需要不同大小的变量，可以使用<code>&lt;stdint.h&gt;</code>中的<font color="#800000">精确宽度（precise-width）</font>的整型，如<code>int16_t</code>。</p>
<p><strong>定义</strong>：C++没有指定整型的大小，通常人们认为<code>short</code>是16位，<code>int</code>是32位，<code>long</code>是32位，<code>long long</code>是64位。</p>
<p><strong>优点</strong>：保持声明统一。</p>
<p><strong>缺点</strong>：C++中整型大小因编译器和体系结构的不同而不同。</p>
<p><strong>结论</strong>：</p>
<p><code>&lt;stdint.h&gt;</code>定义了<code>int16_t</code>、<code>uint32_t</code>、<code>int64_t</code>等整型，在需要确定大小的整型时可以使用它们代替<code>short</code>、<code>unsigned long long</code>等，在C整型中，只使用<code>int</code>。适当情况下，推荐使用标准类型如<code>size_t</code>和<code>ptrdiff_t</code>。</p>
<p>最常使用的是，对整数来说，通常不会用到太大，如循环计数等，可以使用普通的<code>int</code>。你可以认为<code>int</code>至少为32位，但不要认为它会多于32位，需要64位整型的话，可以使用<code>int64_t</code>或<code>uint64_t</code>。</p>
<p>对于大整数，使用<code>int64_t</code>。</p>
<p>不要使用<code>uint32_t</code>等无符号整型，除非你是在表示一个<font color="#800000">位组（bit pattern）</font>而不是一个数值。即使数值不会为负值也不要使用无符号类型，使用<font color="#800000">断言（assertion，译者注，这一点很有道理，计算机只会根据变量、返回值等有无符号确定数值正负，仍然无法确定对错）</font>来保护数据。</p>
<p><strong>无符号整型</strong>：</p>
<p>有些人，包括一些教科书作者，推荐使用无符号类型表示非负数，类型表明了数值取值形式。但是，在C语言中，这一优点被由其导致的bugs所淹没。看看：</p><pre>for (unsigned int i = foo.Length()-1; i &gt;= 0; --i) ...</pre>
<p></p>
<p>上述代码永远不会终止！有时gcc会发现该bug并报警，但通常不会。类似的bug还会出现在比较有符合变量和无符号变量时，主要是C的<font color="#800000">类型提升机制（type-promotion scheme，C语言中各种内建类型之间的提升转换关系）</font>会致使无符号类型的行为出乎你的意料。</p>
<p>因此，使用断言声明变量为非负数，不要使用无符号型。</p>
<p><font color="#800000"><strong>13. 64位下的可移植性（64-bit Portability</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>代码在64位和32位的系统中，原则上应该都比较友好，尤其对于输出、比较、<font color="#800000">结构对齐（structure alignment）</font>来说：</p>
<p>1) <code>printf()</code>指定的一些类型在32位和64位系统上可移植性不是很好，C99标准定义了一些可移植的格式。不幸的是，MSVC 7.1并非全部支持，而且标准中也有所遗漏。所以有时我们就不得不自己定义丑陋的版本（使用标准风格要包含文件<code>inttypes.h</code>）：</p><pre>// printf macros for size_t, in the style of inttypes.h
#ifdef _LP64
#define __PRIS_PREFIX "z"
#else
#define __PRIS_PREFIX
#endif

// Use these macros after a % in a printf format string
// to get correct 32/64 bit behavior, like this:
// size_t size = records.size();
// printf("%"PRIuS"\n", size);

#define PRIdS __PRIS_PREFIX "d"
#define PRIxS __PRIS_PREFIX "x"
#define PRIuS __PRIS_PREFIX "u"
#define PRIXS __PRIS_PREFIX "X"
#define PRIoS __PRIS_PREFIX "o" </pre>
<p></p>
<div align="center">
<table cellspacing="0" cellpadding="1" width="529" align="center" border="0">
<tbody>
<tr>
<td valign="top" width="154">类型</td>
<td valign="top" width="109">不要使用</td>
<td valign="top" width="142">使用</td>
<td valign="top" width="122">备注</td></tr>
<tr>
<td valign="top" width="154"><code>void *</code>（或其他指针类型）</td>
<td valign="top" width="109"><code>%lx</code></td>
<td valign="top" width="142"><code>%p</code></td>
<td valign="top" width="122">&nbsp;</td></tr>
<tr>
<td valign="top" width="154"><code>int64_t</code></td>
<td valign="top" width="109"><code>%qd</code>, <code>%lld</code></td>
<td valign="top" width="142"><code>%"PRId64"</code></td>
<td valign="top" width="122">&nbsp;</td></tr>
<tr>
<td valign="top" width="154"><code>uint64_t</code></td>
<td valign="top" width="109"><code>%qu</code>, <code>%llu</code>, <code>%llx</code></td>
<td valign="top" width="142"><code>%"PRIu64"</code>, <code>%"PRIx64"</code></td>
<td valign="top" width="122">&nbsp;</td></tr>
<tr>
<td valign="top" width="154"><code>size_t</code></td>
<td valign="top" width="109"><code>%u</code></td>
<td valign="top" width="142"><code>%"PRIuS"</code>, <code>%"PRIxS"</code></td>
<td valign="top" width="122">C99指定<code>%zu</code></td></tr>
<tr>
<td valign="top" width="154"><code>ptrdiff_t</code></td>
<td valign="top" width="109"><code>%d</code></td>
<td valign="top" width="142"><code>%"PRIdS"</code></td>
<td valign="top" width="122">C99指定<code>%zd</code></td></tr></tbody></table></div>
<p><br>注意宏<code>PRI*</code>会被编译器扩展为独立字符串，因此如果使用非常量的格式化字符串，需要将宏的值而不是宏名插入格式中，在使用宏<code>PRI*</code>时同样可以在<code>%</code>后指定长度等信息。例如，<code>printf("x = %30"PRIuS"\n", x)</code>在32位Linux上将被扩展为<code>printf("x = %30" "u" "\n", x)</code>，编译器会处理为<code>printf("x = %30u\n", x)</code>。</p>
<p>2) 记住<code>sizeof(void *)</code> != <code>sizeof(int)</code>，如果需要一个指针大小的整数要使用<code>intptr_t</code>。</p>
<p>3) 需要对结构对齐加以留心，尤其是对于存储在磁盘上的结构体。在64位系统中，任何拥有<code>int64_t</code>/<code>uint64_t</code>成员的类/结构体将默认被处理为8字节对齐。如果32位和64位代码共用磁盘上的结构体，需要确保两种体系结构下的结构体的对齐一致。大多数编译器提供了调整结构体对齐的方案。gcc中可使用<code>__attribute__((packed))</code>，MSVC提供了<code>#pragma pack()</code>和<code>__declspec(align())</code><font color="#800000">（译者注，解决方案的项目属性里也可以直接设置）</font>。</p>
<p>4) 创建64位常量时使用<code>LL</code>或<code>ULL</code>作为后缀，如：</p>
<p><pre>int64_t my_value = 0x123456789LL;
uint64_t my_mask = 3ULL &lt;&lt; 48;</pre>
<p></p>
<p>5) 如果你确实需要32位和64位系统具有不同代码，可以在代码变量前使用。（尽量不要这么做，使用时尽量使修改局部化）。</p>
<p><font color="#800000"><strong>14. 预处理宏（Preprocessor Macros</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>使用宏时要谨慎，尽量以内联函数、枚举和常量代替之。</p>
<p>宏意味着你和编译器看到的代码是不同的，因此可能导致异常行为，尤其是当宏存在于全局作用域中。</p>
<p>值得庆幸的是，C++中，宏不像C中那么必要。宏内联<font color="#800000">效率关键代码（performance-critical code）</font>可以内联函数替代；宏存储常量可以<code>const</code>变量替代；宏“缩写”长变量名可以引用替代；使用宏进行条件编译，这个……，最好不要这么做，会令测试更加痛苦（<code>#define</code>防止头文件重包含当然是个例外）。</p>
<p>宏可以做一些其他技术无法实现的事情，在一些代码库（尤其是底层库中）可以看到宏的某些特性（如<font color="#800000">字符串化（stringifying，译者注，使用<code>#</code>）</font>、<font color="#800000">连接（concatenation，译者注，使用<code>##</code>）</font>等等）。但在使用前，仔细考虑一下能不能不使用宏实现同样效果。</p>
<p><strong>译者注：关于宏的高级应用，可以参考</strong><a title="C语言宏的高级应用" href="http://milton.bloghome.cn/posts/171674.html" target="_blank"><strong>C语言宏的高级应用</strong></a><strong>。</strong></p>
<p>下面给出的用法模式可以避免一些使用宏的问题，供使用宏时参考：</p>
<p>1) 不要在<code>.h</code>文件中定义宏；</p>
<p>2) 使用前正确<code>#define</code>，使用后正确<code>#undef</code>；</p>
<p>3) 不要只是对已经存在的宏使用<code>#undef</code>，选择一个不会冲突的名称；</p>
<p>4) 不使用会导致不稳定的<font color="#800000">C++构造（unbalanced C++ constructs，译者注）</font>的宏，至少文档说明其行为。</p>
<p><font color="#800000"><strong>15. 0和NULL（0 and NULL</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>整数用<code>0</code>，实数用<code>0.0</code>，指针用<code>NULL</code>，字符（串）用<code>'\0'</code>。</p>
<p>整数用<code>0</code>，实数用<code>0.0</code>，这一点是毫无争议的。</p>
<p>对于指针（地址值），到底是用<code>0</code>还是<code>NULL</code>，Bjarne Stroustrup建议使用最原始的<code>0</code>，我们建议使用看上去像是指针的<code>NULL</code>，事实上一些C++编译器（如gcc 4.1.0）专门提供了<code>NULL</code>的定义，可以给出有用的警告，尤其是<code>sizeof(NULL)和<code>sizeof(0)</code>不相等</code>的情况。</p>
<p>字符（串）用<code>'\0'</code>，不仅类型正确而且可读性好。</p>
<p><font color="#800000"><strong>16. sizeof（sizeof</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>尽可能用<code>sizeof(<var>varname</var>)</code>代替<code>sizeof(<var>type</var>)</code>。</p>
<p>使用<code>sizeof(<var>varname</var>)</code>是因为当变量类型改变时代码自动同步，有些情况下<code>sizeof(<var>type</var>)</code>或许有意义，还是要尽量避免，如果变量类型改变的话不能同步。</p><pre>Struct data;
memset(&amp;data, 0, sizeof(data));</pre><pre>memset(&amp;data, 0, sizeof(Struct));</pre>
<p></p>
<p><font color="#800000"><strong>17. Boost库（Boost</strong></font><font color="#800000"><strong>）</strong></font></p>
<p>只使用Boost中被认可的库。</p>
<p><strong>定义</strong>：Boost库集是一个非常受欢迎的、<font color="#800000">同级评议的（peer-reviewed）</font>、免费的、开源的C++库。</p>
<p><strong>优点</strong>：Boost代码质量普遍较高、可移植性好，填补了C++标准库很多空白，如<font color="#800000">型别特性（type traits）</font>、更完善的<font color="#800000">绑定（binders）</font>、更好的智能指针，同时还提供了TR1（标准库的扩展）的实现。</p>
<p><strong>缺点</strong>：某些Boost库提倡的编程实践可读性差，像<font color="#800000">元程序（metaprogramming）</font>和其他高级模板技术，以及过度<font color="#800000">“函数化”（"functional"）</font>的编程风格。</p>
<p><strong>结论</strong>：为了向阅读和维护代码的人员提供更好的可读性，我们只允许使用Boost特性的一个成熟子集，当前，这些库包括：</p>
<p>1) <a href="http://www.boost.org/libs/utility/compressed_pair.htm">Compressed Pair</a>：<code>boost/compressed_pair.hpp</code>；</p>
<p>2) <a href="http://www.boost.org/libs/ptr_container/">Pointer Container</a>：<code>boost/ptr_container</code>不包括<code>ptr_array.hpp</code>和序列化（serialization）。</p>
<p>我们会积极考虑添加可以的Boost特性，所以不必拘泥于该规则。</p>
<p>______________________________________ </p>
<p>译者：关于C++特性的注意事项，总结一下： 
<p><strong>1. 对于智能指针，安全第一、方便第二，尽可能局部化（scoped_ptr）</strong><strong>；</strong> 
<p><strong>2. 引用形参加上const，否则使用指针形参；</strong> 
<p><strong>3. 函数重载的使用要清晰、易读；</strong> 
<p><strong>4. 鉴于容易误用，禁止使用缺省函数参数（值得商榷）；</strong> 
<p><strong>5. 禁止使用变长数组；</strong> 
<p><strong>6. 合理使用友元；</strong> 
<p><strong>7. 为了方便代码管理，禁止使用异常（值得商榷）；</strong> 
<p><strong>8. 禁止使用RTTI，否则重新设计代码吧；</strong> 
<p><strong>9. 使用C++风格的类型转换，除单元测试外不要使用dynamic_cast；</strong> 
<p><strong>10. 使用流还printf + read/write，it is a problem；</strong> 
<p><strong>11. 能用前置自增/减不用后置自增/减；</strong> 
<p><strong>12. const能用则用，提倡const在前；</strong></p>
<p><strong>13. 使用确定大小的整型，除位组外不要使用无符号型；</strong></p>
<p><strong>14. 格式化输出及结构对齐时，注意32位和64位的系统差异；</strong></p>
<p><strong>15. 除字符串化、连接外尽量避免使用宏；</strong></p>
<p><strong>16. 整数用0，实数用0.0，指针用NULL，字符（串）用'\0'；</strong></p>
<p><strong>17. 用sizeof(varname)代替sizeof(type)；</strong></p>
<p><strong>18. 只使用Boost中被认可的库。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/56760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-21 14:55 <a href="http://www.cppblog.com/Fox/archive/2008/07/21/56760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>日志该怎么记录？</title><link>http://www.cppblog.com/Fox/archive/2008/07/18/56481.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Fri, 18 Jul 2008 02:03:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/18/56481.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56481.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/18/56481.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56481.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56481.html</trackback:ping><description><![CDATA[<p>一个好的日志系统，除了可以记录尽可能多的必要信息，方便trace bugs、提供data analysis source这些基本功能之外，其他的貌似不必太在意。但真正当bugs冒出来的时候，要命的是既没有dump，也没有有价值的日志，更要命的是日志居然已经记录了那么多，居然让你查了半天，居然都是没有价值的！</p> <p>悲剧啊！</p> <p>日志需要记录的信息大概分为两类：</p> <p><strong>1) 系统运行情况</strong>：启动、加载、读写、关闭、<strong>异常</strong>；</p> <p><strong>2) 用户使用情况</strong>：进入、操作、离开、<strong>异常</strong>。</p> <p>我可以想到的关于日志系统的要求大致以下几点：</p> <p><strong>1) 日志系统使用目录树结构</strong>：系统日志和用户日志分别记录，正常日志和异常日志分别记录，不置于同一文件夹下，日志文件命名做到令观者一目了然；</p> <p><strong>2) 记录详尽但不冗余</strong>：正确记录日志时间、位置、事件、因果，有可能的话，<strong>记录上下文</strong>（这要求有点高了）；</p> <p><strong>3) 格式统一但严禁千篇一律</strong>：格式统一是指记录内容遵循一定格式，方便查看，严禁千篇一律是指记录要有层次、轻重，不同事件导致的“同一”异常日志不应不加区别，同样是为了方便查看；</p> <p><strong>4) 与异常处理相辅相成</strong>：有dump时，以日志辅助快速定位，没有dump时，日志应尽可能提供有效信息，<strong>离系统崩溃的地方越近越好</strong>（这一点似乎也有难度）。</p> <p>________________________________________________</p> <p>突然想到的，也还没有动手去做，先记下了，欢迎补充。</p> <p>_____Added on Jul.25th, 2008_______________________</p> <p>还看到一位兄弟在为我说话，谢谢！</p> <p>今天在考虑实现时，想到一个很现实的问题，日志几乎是无处不在的，随时随地会有日志记录。不知道有谁对I/O（当然主要是Output）消耗和对系统的影响做过专门测试，猜测就算了:-)，我很想知道有没有必要放到专门的线程中，如果放到独立线程中的话，问题就出来了，多长时间写一次？毕竟，记录日志的主要目的就是为了全面记录系统运行和用户使用情况，如果在服务器crash的时候，还有日志（尤其是crash上下文日志）没有被顺利写入，日志的意义也就大打折扣。</p> <p>谁给点建议？</p><img src ="http://www.cppblog.com/Fox/aggbug/56481.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-18 10:03 <a href="http://www.cppblog.com/Fox/archive/2008/07/18/56481.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（三）</title><link>http://www.cppblog.com/Fox/archive/2008/07/16/56324.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 16 Jul 2008 09:43:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/16/56324.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56324.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/16/56324.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56324.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56324.html</trackback:ping><description><![CDATA[<p>这一篇主要提到的是类，Lippman在《<a title="Inside The C++ Object Model" href="www.amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545" target="_blank">Inside The C++ Object Model</a>》第二章中对构造函数作了详尽说明，本文中提到的几个单词基本仿该书中译本侯捷先生的翻译：</p> <p>explicit：明确的</p> <p>implicit：隐含的</p> <p>trivial：没有意义的</p> <p>non-trivial：有意义的</p> <p>&nbsp;</p> <p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">类</font></strong></li></ul> <p>类是C++中基本的代码单元，自然被广泛使用。本节列举了在写一个类时要做什么、不要做什么。</p> <p><strong><font color="#800000">1. 构造函数（Constructor）的职责</font></strong></p> <p>构造函数中只进行那些<font color="#800000">没有实际意义的（trivial，译者注：简单初始化对于程序执行没有实际的逻辑意义，因为成员变量的“有意义”的值大多不在构造函数中确定）</font>初始化，可能的话，使用Init()方法集中初始化为<font color="#800000">有意义的（non-trivial）</font>数据。</p> <p><strong>定义：</strong>在构造函数中执行初始化操作。</p> <p><strong>优点：</strong>排版方便，无需担心类是否初始化。</p> <p><strong>缺点：</strong>在构造函数中执行操作引起的问题有：</p> <p>1) 构造函数中不易报告错误，不能使用异常。</p> <p>2) 操作失败会造成对象初始化失败，引起不确定状态。</p> <p>3) 构造函数内调用虚函数，调用不会派发到子类实现中，即使当前没有子类化实现，将来仍是隐患。</p> <p>4) 如果有人创建该类型的全局变量（虽然违背了上节提到的规则），构造函数将在main()之前被调用，有可能破坏构造函数中暗含的假设条件。例如，gflags尚未初始化。</p> <p><strong>结论：</strong>如果对象需要<font color="#800000">有意义的（non-trivial）</font>初始化，考虑使用另外的Init()方法并（或）增加一个成员标记用于指示对象是否已经初始化成功。</p> <p><strong><font color="#800000">2. 默认构造函数（Default Constructors）</font></strong></p> <p>如果一个类定义了若干成员变量又没有其他构造函数，需要定义一个默认构造函数，否则编译器将自动生产默认构造函数。</p> <p><strong>定义：</strong>新建一个没有参数的对象时，默认构造函数被调用，当调用new[]（为数组）时，默认构造函数总是被调用。</p> <p><strong>优点：</strong>默认将结构体初始化为“不可能的”值，使调试更加容易。</p> <p><strong>缺点：</strong>对代码编写者来说，这是多余的工作。</p> <p><strong>结论：</strong></p> <p>如果类中定义了成员变量，没有提供其他构造函数，你需要定义一个默认构造函数（没有参数）。默认构造函数更适合于初始化对象，使对象<font color="#800000">内部状态（internal state）</font>一致、有效。</p> <p>提供默认构造函数的原因是：如果你没有提供其他构造函数，又没有定义默认构造函数，编译器将为你自动生成一个，编译器生成的构造函数并不会对对象进行初始化。  <p>如果你定义的类继承现有类，而你又没有增加新的成员变量，则不需要为新类定义默认构造函数。  <p><strong><font color="#800000">3. 明确的构造函数（Explicit Constructors）</font></strong></p> <p>对单参数构造函数使用C++关键字explicit。</p> <p><strong>定义：</strong>通常，只有一个参数的构造函数可被用于<font color="#800000">转换（conversion，译者注：主要指隐式转换，下文可见）</font>，例如，定义了Foo::Foo(string name)，当向需要传入一个Foo对象的函数传入一个字符串时，构造函数Foo::Foo(string name)被调用并将该字符串转换为一个Foo临时对象传给调用函数。看上去很方便，但如果你并不希望如此通过转换生成一个新对象的话，麻烦也随之而来。为避免构造函数被调用造成隐式转换，可以将其声明为explicit。</p> <p><strong>优点：</strong>避免不合时宜的变换。  <p><strong>缺点：</strong>无。  <p><strong>结论：</strong>  <p>所有单参数构造函数必须是明确的。在类定义中，将关键字explicit加到单参数构造函数前：explicit Foo(string name);  <p>例外：在少数情况下，拷贝构造函数可以不声明为explicit；特意作为其他类的透明包装器的类。类似例外情况应在注释中明确说明。  <p><strong><font color="#800000">4. 拷贝构造函数（Copy Constructors）</font></strong></p> <p>仅在代码中需要拷贝一个类对象的时候使用拷贝构造函数；不需要拷贝时应使用<code>DISALLOW_COPY_AND_ASSIGN</code>。</p> <p><strong>定义：</strong>通过拷贝新建对象时可使用拷贝构造函数（特别是对象的传值时）。</p> <p><strong>优点：</strong>拷贝构造函数使得拷贝对象更加容易，STL容器要求所有内容可拷贝、可赋值。</p> <p><strong>缺点：</strong>C++中对象的隐式拷贝是导致很多性能问题和bugs的根源。拷贝构造函数降低了代码可读性，相比按引用传递，跟踪按值传递的对象更加困难，对象修改的地方变得难以捉摸。</p> <p><strong>结论：</strong></p> <p>大量的类并不需要可拷贝，也不需要一个拷贝构造函数或<font color="#800000">赋值操作（assignment operator）</font>。不幸的是，如果你不主动声明它们，编译器会为你自动生成，而且是public的。</p> <p>可以考虑在类的private中添加<font color="#800000">空的（dummy）</font>拷贝构造函数和赋值操作，只有声明，没有定义。由于这些空程序声明为private，当其他代码试图使用它们的时候，编译器将报错。为了方便，可以使用宏DISALLOW_COPY_AND_ASSIGN：</p><code> <p>// 禁止使用拷贝构造函数和赋值操作的宏<br>// 应在类的private:中使用<br>#define DISALLOW_COPY_AND_ASSIGN(TypeName) \<br>&nbsp; TypeName(const TypeName&amp;);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp; void operator=(const TypeName&amp;)</p> <p>class Foo {<br>public:<br>&nbsp; Foo(int f);<br>&nbsp; ~Foo();  <p>private:<br>&nbsp; DISALLOW_COPY_AND_ASSIGN(Foo);<br>};</p></code> <p>如上所述，绝大多数情况下都应使用DISALLOW_COPY_AND_ASSIGN，如果类确实需要可拷贝，应在该类的头文件中说明原由，并适当定义拷贝构造函数和赋值操作，注意在operator=中检测<font color="#800000">自赋值（self-assignment）</font>情况。</p> <p>在将类作为STL容器值得时候，你可能有使类可拷贝的冲动。类似情况下，真正该做的是使用指针指向STL容器中的对象，可以考虑使用std::tr1::shared_ptr。</p> <p><strong><font color="#800000">5. 结构体和类（Structs vs. Classes）</font></strong></p> <p>仅当只有数据时使用struct，其它一概使用class。</p> <p>在C++中，关键字struct和class几乎含义等同，我们为其人为添加语义，以便为定义的数据类型合理选择使用哪个关键字。</p> <p>struct被用在仅包含数据的消极对象（passive objects）上，可能包括有关联的常量，但没有存取数据成员之外的函数功能，而存取功能通过直接访问实现而无需方法调用，这儿提到的方法是指只用于处理数据成员的，如构造函数、析构函数、Initialize()、Reset()、Validate()。</p> <p>如果需要更多的函数功能，class更适合，如果不确定的话，直接使用class。</p> <p>如果与STL结合，对于<font color="#800000">仿函数（functors）和特性（traits）</font>可以不用class而是使用struct。</p> <p>注意：类和结构体的成员变量使用不同的命名规则。</p> <p><strong><font color="#800000">6. 继承（Inheritance</font></strong><strong><font color="#800000">）</font></strong></p> <p>使用<font color="#800000">组合（composition，译者注，这一点也是GoF在《Design Patterns》里反复强调的）</font>通常比使用继承更适宜，如果使用继承的话，只使用公共继承。</p> <p><strong>定义：</strong>当子类继承基类时，子类包含了父基类所有数据及操作的定义。C++实践中，继承主要用于两种场合：<font color="#800000">实现继承（implementation inheritance）</font>，子类继承父类的实现代码；<font color="#800000">接口继承（interface inheritance）</font>，子类仅继承父类的方法名称。</p> <p><strong>优点：</strong>实现继承通过原封不动的重用基类代码减少了代码量。由于继承是<font color="#800000">编译时声明（compile-time declaration）</font>，编码者和编译器都可以理解相应操作并发现错误。接口继承可用于程序上增强类的特定API的功能，在类没有定义API的必要实现时，编译器同样可以侦错。</p> <p><strong>缺点：</strong>对于实现继承，由于实现子类的代码在父类和子类间延展，要理解其实现变得更加困难。子类不能重写父类的非虚函数，当然也就不能修改其实现。基类也可能定义了一些数据成员，还要区分基类的<font color="#800000">物理轮廓（physical layout）</font>。</p> <p><strong>结论：</strong></p> <p>所有继承必须是public的，如果想私有继承的话，应该采取包含基类实例作为成员的方式作为替代。 </p> <p>不要过多使用实现继承，组合通常更合适一些。努力做到只在<font color="#800000">“是一个”（"is-a"，译者注，其他"has-a"情况下请使用组合）</font>的情况下使用继承：如果Bar的确“是一种”Foo，才令Bar是Foo的子类。  <p>必要的话，令析构函数为virtual，必要是指，如果该类具有虚函数，其析构函数应该为虚函数。  <p><strong>译者注：至于子类没有额外数据成员，甚至父类也没有任何数据成员的特殊情况下，析构函数的调用是否必要是语义争论，从编程设计规范的角度看，在含有虚函数的父类中，定义虚析构函数绝对必要。</strong>  <p>限定仅在子类访问的成员函数为protected，需要注意的是数据成员应始终为私有。  <p>当重定义派生的虚函数时，在派生类中明确声明其为virtual。根本原因：如果遗漏virtual，阅读者需要检索类的所有祖先以确定该函数是否为虚函数<font color="#800000">（译者注，虽然不影响其为虚函数的本质）</font>。  <p><strong><font color="#800000">7. 多重继承（Multiple Inheritance</font></strong><strong><font color="#800000">）</font></strong></p> <p>真正需要用到<font color="#800000">多重实现继承（multiple implementation inheritance）</font>的时候非常少，只有当最多一个基类中含有实现，其他基类都是以<code>Interface</code>为后缀的纯接口类时才会使用多重继承。</p> <p><strong>定义：</strong>多重继承允许子类拥有多个基类，要将作为纯接口的基类和具有实现的基类区别开来。</p> <p><strong>优点：</strong>相比单继承，多重实现继承可令你重用更多代码。</p> <p><strong>缺点：</strong>真正需要用到多重实现继承的时候非常少，多重实现继承看上去是不错的解决方案，通常可以找到更加明确、清晰的、不同的解决方案。</p> <p><strong>结论：</strong>只有当所有<font color="#800000">超类（superclass）</font>除第一个外都是纯接口时才能使用多重继承。为确保它们是纯接口，这些类必须以<code>Interface</code>为后缀。</p> <p>注意：关于此规则，Windows下有种例外情况（译者注，将在本译文最后一篇的规则例外中阐述）。</p> <p><strong><font color="#800000">8. 接口（Interface</font></strong><strong><font color="#800000">）</font></strong></p> <p>接口是指满足特定条件的类，这些类以Interface为后缀（非必需）。</p> <p><strong>定义：</strong>当一个类满足以下要求时，称之为纯接口：</p> <p>1) 只有纯虚函数（"=0"）和静态函数（下文提到的析构函数除外）；</p> <p>2) 没有非静态数据成员；</p> <p>3) 没有定义任何构造函数。如果有，也不含参数，并且为protected；</p> <p>4) 如果是子类，也只能继承满足上述条件并以Interface为后缀的类。</p> <p>接口类不能被直接实例化，因为它声明了纯虚函数。为确保接口类的所有实现可被正确销毁，必须为之声明虚析构函数（作为第1条规则的例外，析构函数不能是纯虚函数）。具体细节可参考Stroustrup的《<a title="The C++ Programming Language, 3rd edition" href="www.amazon.com/Programming-Language-3rd-Bjarne-Stroustrup/dp/0201889544" target="_blank">The C++ Programming Language, 3rd edition</a>》第12.4节。</p> <p><strong>优点：</strong>以Interface为后缀可令他人知道不能为该接口类增加实现函数或非静态数据成员，这一点对于多重继承尤其重要。另外，对于Java程序员来说，接口的概念已经深入人心。</p> <p><strong>缺点：</strong>Interface后缀增加了类名长度，为阅读和理解带来不便，同时，接口特性作为实现细节不应暴露给客户。</p> <p><strong>结论：</strong>。只有在满足上述需要时，类才以Interface结尾，但反过来，满足上述需要的类未必一定以Interface结尾。</p> <p><strong><font color="#800000">9. 操作符重载（Operator Overloading</font></strong><strong><font color="#800000">）</font></strong></p> <p>除少数特定环境外，不要重载操作符。</p> <p><strong>定义：</strong>一个类可以定义诸如+、/等操作符，使其可以像内建类型一样直接使用。</p> <p><strong>优点：</strong>使代码看上去更加直观，就像内建类型（如int）那样，重载操作符使那些Equals()、Add()等黯淡无光的函数名好玩多了。为了使一些模板函数正确工作，你可能需要定义操作符。</p> <p><strong>缺点：</strong>虽然操作符重载令代码更加直观，但也有一些不足</p> <p>1) 混淆直觉，让你误以为一些耗时的操作像内建操作那样轻巧；</p> <p>2) 查找重载操作符的调用处更加困难，查找Equals()显然比同等调用==容易的多；</p> <p>3) 有的操作符可以对指针进行操作，容易导致bugs，Foo + 4做的是一件事，而&amp;Foo + 4可能做的是完全不同的另一件事，对于二者，编译器都不会报错，使其很难调试；</p> <p>4) 重载还有令你吃惊的副作用，比如，重载操作符&amp;的类不能被前置声明。 </p> <p><strong>结论：</strong></p> <p>一般不要重载操作符，尤其是赋值操作（operator=）比较阴险，应避免重载。如果需要的话，可以定义类似Equals()、CopyFrom()等函数。</p> <p>然而，极少数情况下需要重载操作符以便与模板或“标准”C++类衔接（如operator&lt;&lt;(ostream&amp;, const T&amp;)），如果被证明是正当的尚可接受，但你要尽可能避免这样做。尤其是不要仅仅为了在STL容器中作为key使用就重载operator==或operator&lt;，取而代之，你应该在声明容器的时候，创建相等判断和大小比较的仿函数类型。</p> <p>有些STL算法确实需要重载operator==时可以这么做，不要忘了提供文档说明原因。</p> <p>参考<strong>拷贝构造函数</strong>和<strong>函数重载</strong>。 </p> <p><strong><font color="#800000">10. 存取控制（Access Control</font></strong><strong><font color="#800000">）</font></strong></p> <p>将数据成员私有化，并提供相关存取函数，如定义变量foo_及取值函数foo()、赋值函数set_foo()。</p> <p>存取函数的定义一般内联在头文件中。</p> <p>参考<strong>继承</strong>和<strong>函数命名</strong>。</p> <p><strong><font color="#800000">11. 声明次序（Declaration Order</font></strong><strong><font color="#800000">）</font></strong></p> <p>在类中使用特定的声明次序：public:在private:之前，成员函数在数据成员（变量）前。</p> <p>定义次序如下：public:、protected:、private:，如果那一块没有，直接忽略即可。</p> <p>每一块中，声明次序一般如下：</p> <p>1) typedefs和enums；</p> <p>2) 常量；</p> <p>3) 构造函数；</p> <p>4) 析构函数；</p> <p>5) 成员函数，含静态成员函数；</p> <p>6) 数据成员，含静态数据成员。</p> <p>宏DISALLOW_COPY_AND_ASSIGN置于private:块之后，作为类的最后部分。参考<strong>拷贝构造函数</strong>。</p> <p>.cc文件中函数的定义应尽可能和声明次序一致。</p> <p>不要将大型函数内联到类的定义中，通常，只有那些没有特别意义的或者性能要求高的，并且是比较短小的函数才被定义为内联函数。更多细节参考<a title="[译]Google C++编程风格指南（一）" href="http://www.cppblog.com/Fox/archive/2008/07/10/55818.html" target="_blank">译文第一篇的</a><strong>内联函数</strong>。</p> <p><strong><font color="#800000">12. 编写短小函数（Write Short Functions</font></strong><strong><font color="#800000">）</font></strong></p> <p>倾向于选择短小、凝练的函数。</p> <p>长函数有时是恰当的，因此对于函数长度并没有严格限制。如果函数超过40行，可以考虑在不影响程序结构的情况下将其分割一下。</p> <p>即使一个长函数现在工作的非常好，一旦有人对其修改，有可能出现新的问题，甚至导致难以发现的bugs。使函数尽量短小、简单，便于他人阅读和修改代码。</p> <p>在处理代码时，你可能会发现复杂的长函数，不要害怕修改现有代码：如果证实这些代码使用、调试困难，或者你需要使用其中的一小块，考虑将其分割为更加短小、易于管理的若干函数。</p> <p>______________________________________ </p> <p>译者：关于类的注意事项，总结一下： <p><strong>1. 不在构造函数中做太多逻辑相关的初始化；</strong>  <p><strong>2. 编译器提供的默认构造函数不会对变量进行初始化，如果定义了其他构造函数，编译器不再提供，需要编码者自行提供默认构造函数；</strong>  <p><strong>3. 为避免隐式转换，需将单参数构造函数声明为explicit；</strong>  <p><strong>4. 为避免拷贝构造函数、赋值操作的滥用和编译器自动生成，可目前声明其为private且无需实现；</strong> <p><strong>5. 仅在作为数据集合时使用struct；</strong> <p><strong>6. 组合&gt;实现继承&gt;接口继承&gt;私有继承，子类重载的虚函数也要声明virtual关键字，虽然编译器允许不这样做；</strong> <p><strong>7. 避免使用多重继承，使用时，除一个基类含有实现外，其他基类均为纯接口；</strong> <p><strong>8. 接口类类名以Interface为后缀，除提供带实现的虚析构函数、静态成员函数外，其他均为纯虚函数，不定义非静态数据成员，不提供构造函数，提供的话，声明为protected；</strong> <p><strong>9. 为降低复杂性，尽量不重载操作符，模板、标准类中使用时提供文档说明；</strong> <p><strong>10. 存取函数一般内联在头文件中；</strong> <p><strong>11. 声明次序：public-&gt;protected-&gt;private；</strong> <p><strong>12. 函数体尽量短小、紧凑，功能单一。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/56324.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-16 17:43 <a href="http://www.cppblog.com/Fox/archive/2008/07/16/56324.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（二）</title><link>http://www.cppblog.com/Fox/archive/2008/07/14/56109.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 14 Jul 2008 07:49:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/14/56109.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/56109.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/14/56109.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/56109.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/56109.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">作用域</font></strong></li></ul> <p><strong><font color="#800000">1. 命名空间（Namespaces）</font></strong></p> <p>在.cc文件中，提倡使用<font color="#800000">不具名的命名空间（unnamed namespaces，译者注：不具名的命名空间就像不具名的类一样，似乎被介绍的很少:-(）</font>。使用具名命名空间时，其名称可基于项目或路径名称，不要使用using指示符。</p> <p><strong>定义：</strong>命名空间将全局作用域细分为不同的、具名的作用域，可有效防止全局作用域的命名冲突。</p> <p><strong>优点：</strong>命名空间提供了（可嵌套）<font color="#800000">命名轴线（name axis，译者注</font><font color="#800000">：将命名分割在不同命名空间内）</font>，当然，类也提供了（可嵌套）的命名轴线（<font color="#800000">译者注：将命名分割在不同类的作用域内</font>）。</p> <p>举例来说，两个不同项目的全局作用域都有一个类Foo，这样在编译或运行时造成冲突。如果每个项目将代码置于不同命名空间中，project1::Foo和project2::Foo作为不同符号自然不会冲突。</p> <p><strong>缺点：</strong>命名空间具有迷惑性，因为它们和类一样提供了额外的（可嵌套的）命名轴线。在头文件中使用不具名的空间容易违背C++的<font color="#800000">唯一定义原则（One Definition Rule (ODR)）</font>。</p> <p><strong>结论：</strong>根据下文将要提到的策略合理使用命名空间。</p> <p><strong><font color="#800000">1) 不具名命名空间（Unnamed Namespaces）</font></strong></p> <p>在.cc文件中，允许甚至提倡使用不具名命名空间，以避免运行时的命名冲突：</p> <p>namespace {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // .cc 文件中  <p>// 命名空间的内容无需缩进<br>enum { UNUSED, EOF, ERROR };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 经常使用的符号<br>bool AtEof() { return pos_ == EOF; }&nbsp;&nbsp; // 使用本命名空间内的符号EOF  <p>}&nbsp; // namespace  <p>然而，与特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函数，而不是不具名命名空间的成员。像上文展示的那样，不具名命名空间结束时用注释// namespace标识。  <p>不能在.h文件中使用不具名命名空间。  <p><font color="#800000"><strong>2) 具名命名空间（Named Namespaces）</strong></font>  <p>具名命名空间使用方式如下：</p> <p>命名空间将<font color="#800000">除文件包含、全局标识的声明/定义以及类的前置声明外</font>的整个源文件封装起来，以同其他命名空间相区分。</p> <p>// .h文件<br>namespace mynamespace { </p> <p>// 所有声明都置于命名空间中<br>// 注意不要使用缩进<br>class MyClass {<br>public:<br>&nbsp; ...<br>&nbsp; void Foo();<br>};  <p>}&nbsp; // namespace mynamespace</p> <p>// .cc文件<br>namespace mynamespace { </p> <p>// 函数定义都置于命名空间中<br>void MyClass::Foo() {<br>&nbsp; ...<br>}  <p>}&nbsp; // namespace mynamespace  <p>通常的.cc文件会包含更多、更复杂的细节，包括对其他命名空间中类的引用等。  <p>#include "a.h"  <p>DEFINE_bool(someflag, false, "dummy flag");  <p>class C;&nbsp; // 全局命名空间中类C的前置声明<br>namespace a { class A; }&nbsp; // 命名空间a中的类a::A的前置声明  <p>namespace b {  <p>...code for b...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // b中的代码  <p>}&nbsp; // namespace b</p> <p>不要声明命名空间std下的任何内容，包括标准库类的前置声明。声明std下的实体会导致不明确的行为，如，不可移植。声明标准库下的实体，需要包含对应的头文件。</p> <p>最好不要使用using指示符，以保证命名空间下的所有名称都可以正常使用。</p> <p>// 禁止——污染命名空间<br>using namespace foo;</p> <p>在.cc文件、.h文件的函数、方法或类中，可以使用using。</p> <p>// 允许：.cc文件中<br>// .h文件中，必须在函数、方法或类的内部使用<br>using ::foo::bar;</p> <p>在.cc文件、.h文件的函数、方法或类中，还可以使用命名空间别名。</p>// 允许：.cc文件中<br>// .h文件中，必须在函数、方法或类的内部使用<br> <p>namespace fbz = ::foo::bar::baz;</p> <p><strong><font color="#800000">2. 嵌套类（Nested Class）</font></strong></p> <p>当公开嵌套类作为接口的一部分时，虽然可以直接将他们保持在全局作用域中，但将嵌套类的声明置于命名空间中是更好的选择。</p> <p><strong>定义：</strong>可以在一个类中定义另一个类，嵌套类也称<font color="#800000">成员类（member class）</font>。  <p>class Foo {  <p>private:<br>&nbsp; // Bar是嵌套在Foo中的成员类<br>&nbsp; class Bar {<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp; };  <p>};  <p><strong>优点：</strong>当嵌套（成员）类只在<font color="#800000">被嵌套类（enclosing class）</font>中使用时很有用，将其置于被嵌套类作用域作为被嵌套类的成员不会污染其他作用域同名类。可在被嵌套类中前置声明嵌套类，在.cc文件中定义嵌套类，避免在被嵌套类中包含嵌套类的定义，因为嵌套类的定义通常只与实现相关。  <p><strong>缺点：</strong>只能在被嵌套类的定义中才能前置声明嵌套类。因此，任何使用Foo::Bar*指针的头文件必须包含整个Foo的声明。  <p><strong>结论：</strong>不要将嵌套类定义为public，除非它们是接口的一部分，比如，某个方法使用了这个类的一系列选项。  <p><font color="#800000"><strong>3. 非成员函数（Nonmember）、静态成员函数（Static Member）和全局函数（Global Functions）</strong></font>  <p>使用命名空间中的非成员函数或静态成员函数，尽量不要使用全局函数。  <p><strong>优点：</strong>某些情况下，非成员函数和静态成员函数是非常有用的，将非成员函数置于命名空间中可避免对全局作用域的污染。  <p><strong>缺点：</strong>将非成员函数和静态成员函数作为新类的成员或许更有意义，当它们需要访问外部资源或具有重要依赖时更是如此。  <p><strong>结论：</strong>  <p>有时，不把函数限定在类的实体中是有益的，甚至需要这么做，要么作为静态成员，要么作为非成员函数。非成员函数不应依赖于外部变量，并尽量置于某个命名空间中。相比单纯为了封装若干不共享任何静态数据的静态成员函数而创建类，不如使用命名空间。  <p>定义于同一编译单元的函数，被其他编译单元直接调用可能会引入不必要的耦合和连接依赖；静态成员函数对此尤其敏感。可以考虑提取到新类中，或者将函数置于独立库的命名空间中。  <p>如果你确实需要定义非成员函数，又只是在.cc文件中使用它，可使用不具名命名空间或static关联（如<code>static int Foo() {...}</code>）限定其作用域。  <p><strong><font color="#800000">4. 局部变量（Local Variables）</font></strong>  <p>将函数变量尽可能置于最小作用域内，在声明变量时将其初始化。</p> <p>C++允许在函数的任何位置声明变量。我们提倡在尽可能小的作用域中声明变量，离第一次使用越近越好。这使得代码易于阅读，易于定位变量的声明位置、变量类型和初始值。特别是，应使用初始化代替声明+赋值的方式。</p> <p>int i;<br>i = f();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 坏——初始化和声明分离<br>nt j = g();&nbsp;&nbsp; // 好——初始化时声明</p> <p>注意：gcc可正确执行for (int i = 0; i &lt; 10; ++i)（i的作用域仅限for循环），因此其他for循环中可重用i。if和while等语句中，<font color="#800000">作用域声明（scope declaration）</font>同样是正确的。</p> <p>while (const char* p = strchr(str, '/')) str = p + 1;</p> <p>注意：如果变量是一个对象，每次进入作用域都要调用其构造函数，每次退出作用域都要调用其析构函数。</p> <p>// 低效的实现<br>for (int i = 0; i &lt; 1000000; ++i) {<br>&nbsp; Foo f;&nbsp; // 构造函数和析构函数分别调用1000000次！<br>&nbsp; f.DoSomething(i);<br>}</p> <p>类似变量放到循环作用域外面声明要高效的多：</p> <p>Foo f;&nbsp; // 构造函数和析构函数只调用1次<br>for (int i = 0; i &lt; 1000000; ++i) {<br>&nbsp; f.DoSomething(i);<br>}</p> <p><strong><font color="#800000">5. 全局变量（Global Variables）</font></strong></p> <p>class类型的全局变量是被禁止的，内建类型的全局变量是允许的，当然多线程代码中非常数全局变量也是被禁止的。永远不要使用函数返回值初始化全局变量。</p> <p>不幸的是，全局变量的构造函数、析构函数以及初始化操作的调用顺序只是被部分规定，每次生成有可能会有变化，从而导致难以发现的bugs。</p> <p>因此，禁止使用class类型的全局变量（包括STL的string, vector等等），因为它们的初始化顺序有可能导致构造出现问题。内建类型和由内建类型构成的没有构造函数的结构体可以使用，如果你一定要使用class类型的全局变量，请使用<font color="#800000">单件模式（singleton pattern）</font>。</p> <p>对于全局的字符串常量，使用C风格的字符串，而不要使用STL的字符串：</p> <p>const char kFrogSays[] = "ribbet";</p> <p>虽然允许在全局作用域中使用全局变量，使用时务必三思。大多数全局变量应该是类的静态数据成员，或者当其只在.cc文件中使用时，将其定义到不具名命名空间中，或者使用静态关联以限制变量的作用域。</p> <p>记住，静态成员变量视作全局变量，所以，也不能是class类型！</p> <p>______________________________________ </p> <p>译者：这一篇主要提到的是作用域的一些规则，总结一下：  <p><strong>1. .cc中的不具名命名空间可避免命名冲突、限定作用域，避免直接使用using提示符污染命名空间；</strong>  <p><strong>2. 嵌套类符合局部使用原则，只是不能在其他头文件中前置声明，尽量不要public；</strong>  <p><strong>3. 尽量不用全局函数和全局变量，考虑作用域和命名空间限制，尽量单独形成编译单元；</strong>  <p><strong>4. 多线程中的全局变量（含静态成员变量）不要使用class类型（含STL容器），避免不明确行为导致的bugs</strong><strong>。</strong>  <p><strong>作用域的使用，除了考虑名称污染、可读性之外，主要是为降低耦合度，提高编译、执行效率。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/56109.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-14 15:49 <a href="http://www.cppblog.com/Fox/archive/2008/07/14/56109.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[译]Google C++编程风格指南（一）</title><link>http://www.cppblog.com/Fox/archive/2008/07/10/55818.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Thu, 10 Jul 2008 09:35:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/07/10/55818.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/55818.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/07/10/55818.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/55818.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/55818.html</trackback:ping><description><![CDATA[<p>原文地址：<a title="Google C++ Style Guide" href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Displaying_Hidden_Details_in_this_Guide" target="_blank" rel="tag">Google C++ Style Guide</a></p> <ul> <li><strong><font color="#800000">背景</font></strong></li></ul> <p>Google的开源项目大多使用C++开发。每一个C++程序员也都知道，C++具有很多强大的语言特性，但这种强大不可避免的导致它的复杂，这种复杂会使得代码更易于出现bug、难于阅读和维护。</p> <p>本指南的目的是通过详细阐述在C++编码时要怎样写、不要怎样写来规避其复杂性。这些规则可在允许代码有效使用C++语言特性的同时使其易于管理。</p> <p>风格，也被视为可读性，主要指称管理C++代码的习惯。使用术语风格有点用词不当，因为这些习惯远不止源代码文件格式这么简单。</p> <p>使代码易于管理的方法之一是增强代码一致性，让别人可以读懂你的代码是很重要的，保持统一编程风格意味着可以轻松根据“模式匹配”规则推断各种符号的含义。创建通用的、必需的习惯用语和模式可以使代码更加容易理解，在某些情况下改变一些编程风格可能会是好的选择，但我们还是应该遵循一致性原则，尽量不这样去做。</p> <p>本指南的另一个观点是C++特性的臃肿。C++是一门包含大量高级特性的巨型语言，某些情况下，我们会限制甚至禁止使用某些特性使代码简化，避免可能导致的各种问题，指南中列举了这类特性，并解释说为什么这些特性是被限制使用的。</p> <p>由Google开发的开源项目将遵照本指南约定。</p> <p>注意：本指南并非C++教程，我们假定读者已经对C++非常熟悉。</p> <ul> <li><strong><font color="#800000">头文件</font></strong></li></ul> <p>通常，每一个.cc文件（C++的源文件）都有一个对应的.h文件（头文件），也有一些例外，如单元测试代码和只包含main()的.cc文件。</p> <p>正确使用头文件可令代码在可读性、文件大小和性能上大为改观。</p> <p>下面的规则将引导你规避使用头文件时的各种麻烦。</p> <p><strong><font color="#800000">1. #define的保护</font></strong></p> <p>所有头文件都应该使用#define防止头文件被<font color="#800000">多重包含（multiple inclusion）</font>，命名格式当是：<code><i>&lt;PROJECT&gt;</i>_<i>&lt;PATH&gt;</i>_<i>&lt;FILE&gt;</i>_H_</code></p> <p><code>为保证唯一性，头文件的命名应基于其所在项目源代码树的全路径。例如，项目foo中的头文件<code>foo/src/bar/baz.h</code>按如下方式保护：</code></p> <p>#ifndef FOO_BAR_BAZ_H_<br>#define FOO_BAR_BAZ_H_<br>...<br>#endif // FOO_BAR_BAZ_H_</p> <p><strong><font color="#800000">2. 头文件依赖</font></strong></p> <p>使用<font color="#800000">前置声明（forward declarations）</font>尽量减少.h文件中#include的数量。</p> <p>当一个头文件被包含的同时也引入了一项新的<font color="#800000">依赖（dependency）</font>，只要该头文件被修改，代码就要重新编译。如果你的头文件包含了其他头文件，这些头文件的任何改变也将导致那些包含了你的头文件的代码重新编译。因此，我们宁可尽量少包含头文件，尤其是那些包含在其他头文件中的。</p> <p>使用前置声明可以显著减少需要包含的头文件数量。举例说明：头文件中用到类File，但不需要访问File的声明，则头文件中只需前置声明<code>class File;</code>无需<code>#include "file/base/file.h"</code>。</p> <p>在头文件如何做到使用类Foo而无需访问类的定义？</p> <p>1) 将数据成员类型声明为Foo *或Foo &amp;；</p> <p>2) 参数、返回值类型为Foo的函数只是声明（但不定义实现）； </p> <p>3) 静态数据成员的类型可以被声明为Foo，因为静态数据成员的定义在类定义之外。</p> <p>另一方面，如果你的类是Foo的子类，或者含有类型为Foo的非静态数据成员，则必须为之包含头文件。</p> <p>有时，使用指针成员（pointer members，如果是<code>scoped_ptr更好</code>）替代对象成员（object members）的确更有意义。然而，这样的做法会降低代码可读性及执行效率。如果仅仅为了少包含头文件，还是不要这样替代的好。</p> <p>当然，.cc文件无论如何都需要所使用类的定义部分，自然也就会包含若干头文件。</p> <p><strong>译者注：能依赖声明的就不要依赖定义。</strong></p> <p><strong><font color="#800000">3. 内联函数</font></strong></p> <p>只有当函数只有10行甚至更少时才会将其定义为<font color="#800000">内联函数（inline function）</font>。</p> <p><strong>定义（Definition）：</strong>当函数被声明为内联函数之后，编译器可能会将其内联展开，无需按通常的函数调用机制调用内联函数。</p> <p><strong>优点：</strong>当函数体比较小的时候，内联该函数可以令目标代码更加高效。对于<font color="#800000">存取函数（accessor、</font><font color="#800000">mutator）</font>以及其他一些比较短的关键执行函数。</p> <p><strong>缺点：</strong>滥用内联将导致程序变慢，内联有可能是目标代码量或增或减，这取决于被内联的函数的大小。内联较短小的存取函数通常会减少代码量，但内联一个很大的函数（译者注：如果编译器允许的话）将戏剧性的增加代码量。在现代处理器上，由于更好的利用<font color="#800000">指令缓存（instruction cache）</font>，小巧的代码往往执行更快。</p> <p><strong>结论：</strong>一个比较得当的处理规则是，不要内联超过10行的函数。对于析构函数应慎重对待，析构函数往往比其表面看起来要长，因为有一些隐式成员和基类析构函数（如果有的话）被调用！</p> <p>另一有用的处理规则：内联那些包含循环或switch语句的函数是得不偿失的，除非在大多数情况下，这些循环或switch语句从不执行。</p> <p>重要的是，虚函数和递归函数即使被声明为内联的也不一定就是内联函数。通常，递归函数不应该被声明为内联的（<font color="#800000">译者注：递归调用堆栈的展开并不像循环那么简单，比如递归层数在编译时可能是未知的，大多数编译器都不支持内联递归函数</font>）。析构函数内联的主要原因是其定义在类的定义中，为了方便抑或是对其行为给出文档。</p> <p><strong><font color="#800000">4. -inl.h文件</font></strong></p> <p>复杂的内联函数的定义，应放在后缀名为-inl.h的头文件中。</p> <p>在头文件中给出内联函数的定义，可令编译器将其在调用处内联展开。然而，实现代码应完全放到.cc文件中，我们不希望.h文件中出现太多实现代码，除非这样做在可读性和效率上有明显优势。</p> <p>如果内联函数的定义比较短小、逻辑比较简单，其实现代码可以放在.h文件中。例如，存取函数的实现理所当然都放在类定义中。出于实现和调用的方便，较复杂的内联函数也可以放到.h文件中，如果你觉得这样会使头文件显得笨重，还可以将其分离到单独的-inl.h中。这样即把实现和类定义分离开来，当需要时包含实现所在的-inl.h即可。</p> <p>-inl.h文件还可用于函数模板的定义，从而使得模板定义可读性增强。</p> <p>要提醒的一点是，-inl.h和其他头文件一样，也需要#define保护。</p> <p><strong><font color="#800000">5. 函数参数顺序（Function Parameter Ordering）</font></strong></p> <p>定义函数时，参数顺序为：输入参数在前，输出参数在后。</p> <p>C/C++函数参数分为输入参数和输出参数两种，有时输入参数也会输出（译者注：值被修改时）。输入参数一般传值或<font color="#800000">常数引用（const references）</font>，输出参数或输入/输出参数为<font color="#800000">非常数指针（non-const pointers）</font>。对参数排序时，将所有输入参数置于输出参数之前。不要仅仅因为是新添加的参数，就将其置于最后，而应该依然置于输出参数之前。</p> <p>这一点并不是必须遵循的规则，输入/输出两用参数（通常是类/结构体变量）混在其中，会使得规则难以遵循。</p> <p><font color="#800000"><strong>6. 包含文件的名称及次序</strong></font></p> <p>将包含次序标准化可增强可读性、避免<font color="#800000">隐藏依赖（hidden dependencies，译者注：隐藏依赖主要是指包含的文件中编译时）</font>，次序如下：C库、C++库、其他库的.h、项目内的.h。</p> <p>项目内头文件应按照项目源代码目录树结构排列，并且避免使用UNIX文件路径.（当前目录）和..（父目录）。例如，google-awesome-project/src/base/logging.h应像这样被包含：</p> <p>#include "base/logging.h"</p> <p>dir/foo.cc的主要作用是执行或测试dir2/foo2.h的功能，foo.cc中包含头文件的次序如下：</p> <p>&nbsp;&nbsp;&nbsp; dir2/foo2.h（优先位置，详情如下）<br>&nbsp;&nbsp;&nbsp; C系统文件<br>&nbsp;&nbsp;&nbsp; C++系统文件<br>&nbsp;&nbsp;&nbsp; 其他库头文件<br>&nbsp;&nbsp;&nbsp; 本项目内头文件</p> <p>这种排序方式可有效减少隐藏依赖，我们希望每一个头文件独立编译。最简单的实现方式是将其作为第一个.h文件包含在对应的.cc中。</p> <p>dir/foo.cc和dir2/foo2.h通常位于相同目录下（像base/basictypes_unittest.cc和base/basictypes.h），但也可在不同目录下。</p> <p>相同目录下头文件按字母序是不错的选择。</p> <p>举例来说，google-awesome-project/src/foo/internal/fooserver.cc的包含次序如下：</p> <p>#include "foo/public/fooserver.h"&nbsp; // 优先位置  <p>#include &lt;sys/types.h&gt;<br>#include &lt;unistd.h&gt;  <p>#include &lt;hash_map&gt;<br>#include &lt;vector&gt;  <p>#include "base/basictypes.h"<br>#include "base/commandlineflags.h"<br>#include "foo/public/bar.h"  <p>______________________________________  <p>译者：英语不太好，翻译的也就不太好。这一篇主要提到的是头文件的一些规则，总结一下：  <p><strong>1. 避免多重包含是学编程时最基本的要求；</strong>  <p><strong>2. 前置声明是为了降低编译依赖，防止修改一个头文件引发多米诺效应；</strong>  <p><strong>3. 内联函数的合理使用可提高代码执行效率；</strong>  <p><strong>4. -inl.h可提高代码可读性（一般用不到吧:D）；</strong>  <p><strong>5. 标准化函数参数顺序可以提高可读性和易维护性（对函数参数的堆栈空间有轻微影响，我以前大多是相同类型放在一起）；</strong>  <p><strong>6. 包含文件的名称使用.和..虽然方便却易混乱，使用比较完整的项目路径看上去很清晰、很条理，包含文件的次序除了美观之外，最重要的是可以减少隐藏依赖，使每个头文件在“最需要编译”（对应源文件处:D）的地方编译，有人提出库文件放在最后，这样出错先是项目内的文件，头文件都放在对应源文件的最前面，这一点足以保证内部错误的及时发现了。</strong></p><img src ="http://www.cppblog.com/Fox/aggbug/55818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-07-10 17:35 <a href="http://www.cppblog.com/Fox/archive/2008/07/10/55818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Debian开发学习日记（二）：系统简单配置</title><link>http://www.cppblog.com/Fox/archive/2008/06/06/debian_simple_configuration.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Thu, 05 Jun 2008 17:33:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/06/06/debian_simple_configuration.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/52317.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/06/06/debian_simple_configuration.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/52317.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/52317.html</trackback:ping><description><![CDATA[<p>sudo使普通用户具有root用户的权限，减少root登录，还有日志记录&#8230;&#8230;，主要是为了安全，暂时没有体会@.@。</p>
<p><strong>1. sudo</strong></p>
<p>1) 安装sudo：<font color=#800000>apt-get install sudo</font></p>
<p>2) 添加sudo：</p>
<p>&nbsp; a. <font color=#800000>chmod +w /etc/sudoers</font>，允许写<br>&nbsp; b. <font color=#800000>vim /etc/sudoers</font><br>&nbsp; c. 添加：<font color=#800000>newsudo&nbsp;&nbsp; ALL=(ALL) NOPASSWD: ALL</font><br>&nbsp; d. <font color=#800000>chmod 0440 /etc/sudoers</font>，更改sudoers文件权限，使之可以修改如vimrc等文件</p>
<p><strong>2. 配置Vim</strong></p>
<p>1) 安装最新Vim：<font color=#800000>apt-get install vim</font></p>
<p>2) 语法加亮：<font color=#800000>vim /etc/vim/vimrc</font>，去掉<font color=#800000>systax on</font>前的注释"</p>
<p>3) 显示行号：<font color=#800000>set nu</font></p>
<p>4) 自动缩进：<font color=#800000>set autoindent</font></p>
<p>&nbsp; C语言缩进：<font color=#800000>set cindent</font></p>
<p><strong>3. 配置Profile</strong></p>
<p>1) <font color=#800000>vim /etc/profile</font></p>
<p>2) 添加：<font color=#800000>alias ls='ls --color=auto'</font></p>
<p>3) <font color=#800000>logout</font></p>
<p>更多零零散散的配置就不写了，因为有TX在我的上一篇回帖说我写的很水，说实话，我有点被冤枉。就我目前对Debian的认知水平，也就到这份上了。有句话说的好：<strong>工欲善其事，必先利其器</strong>。这次哥哥我重新杀回到Debian来，绝对不会只是玩玩儿，<strong>我会负责的</strong>。</p>
<p>后面就会开始用<font color=#800000>make</font>慢慢写一些东西了。说到底，这一系列日记只是对工具的一个熟悉过程而已。</p>
<p>PS：</p>
<p>GR今天在说，我似乎应该把时间花到有用的东西上面，言下之意，我当下做的事没什么意义，呵呵，其实，转到Linux平台下熟悉一下，也算是有意义的事，对我来讲，闲的无聊的时候看毛片也算有意义的事:D。</p>
<p>当然，话说回来，最近关乎学习的另外一件有意义的事情是看看Kevin推荐的《<a href="http://www.amazon.com/Templates-Complete-Guide-David-Vandevoorde/dp/0201734842" target=_blank>C++ Templates</a>》和《<a href="http://www.amazon.com/Modern-Design-Programming-Patterns-Depth/dp/0201704315" target=_blank>Modern C++ Design</a>》。</p>
<p>之所以婆婆妈妈的写下来，主要因为我这个人羞耻感不强 + 健忘，写下来还不去做会让我觉得难堪;-)。</p><img src ="http://www.cppblog.com/Fox/aggbug/52317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-06-06 01:33 <a href="http://www.cppblog.com/Fox/archive/2008/06/06/debian_simple_configuration.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Debian开发学习日记（一）：使用Vim</title><link>http://www.cppblog.com/Fox/archive/2008/06/05/linux_vim.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 04 Jun 2008 16:21:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/06/05/linux_vim.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/52203.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/06/05/linux_vim.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/52203.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/52203.html</trackback:ping><description><![CDATA[<p><font color=#800000><strong>题记：哥（我）基本是Linux盲，刚开始接触，有点像对females的亢奋感。</strong></font></p>
<p>心血来潮，再次装了<font color=#800000>VMware</font>，两个机子分别装了简体中文和英文的<font color=#800000>Debian</font>，只有<font color=#800000>base-system</font>。</p>
<p>前前后后装了不下五、六遍，算是对Debian的安装过程熟悉一下，有点弱智:D。装好系统之后，只装了<font color=#800000>Vim</font>：<font color=#800000>apt-get install vim</font></p>
<p><font color=#800000>Vim</font>是一个很好玩的东西，自从我在06年结束了TC 2.0的时代之后，已经很久没有在纯文本模式下做过任何操作了（偶尔的装过Debian，用过Vim都不算吧:D）。虽然我一向声称更喜欢命令操作而不是鼠标移动，但那毕竟是在用了六、七年的Windows下（虽然也不长:(），看来多少是有点叶公好龙的感觉。</p>
<p>在<font color=#800000>INSERT mode</font>下，费了半天劲，敲了十来行代码，总觉得自己的操作太笨。<a href="http://www.google.com/" target=_blank>Google</a>了一下，国内有一个比较好的专门介绍<a href="http://vcd.gro.clinux.org/" target=_blank>Vim中文文档的site</a>。</p>
<p>原来，在<font color=#800000>NORMAL mode</font>下，<font color=#800000>:help</font>可以搞定一切。只是这个help太长了，一时半会儿看不完。退出Vim（<font color=#800000>:q!</font>）后，命令行运行<font color=#800000>vimtutor</font>是一个基本教程，还有中文版的，打开全是乱码。</p>
<p><font color=#800000>apt-get install zhcon</font></p>
<p>期间要我插入netinst的disk，但我是用VMware，干脆把<font color=#800000>/etc/apt/sources.list</font>的cdrom去掉，直接用网络了:-(。</p>
<p>装好之后，<font color=#800000>/usr/share/vim/vim70/tutor</font>下的<font color=#800000>tutor.zh.big5</font>和<font color=#800000>tutor.zh.euc</font>还是乱码显示，用Vim打开，<font color=#800000>:set fileencoding=utf-8</font>之后也一样，难道是因为我选择的英文版吗？后面看了<a href="http://apt.nc.hcc.edu.tw/docs/debian_X/" target=_blank>tw一篇非常详尽的图解</a>，获益匪浅。原来是没有启动zhcon#--"！</p>
<p>这儿有个小插曲，执行zhcon，机器就挂掉了，屡试不爽。<a href="http://www.google.com/" target=_blank>Google</a>到的解决方案：<font color=#800000>zhcon --utf8 --drv=vga</font></p>
<p>感觉执行zhcon之后，文本显示速度巨慢，慢到不可忍受，遂放弃，就当学习英文好了:-)。</p>
<p>之后，把笔记本上的Debian装成简体中文，晚上回来后，笔记本不能上网了，拿GR的机器装Debian，发现用的netinst的iso总是出错，近乎崩溃。该死的艾普宽带又绑定MAC，笔记本上不成网。只好乖乖的学习Vim的使用:D。</p>
<p>执行<font color=#800000>vimtutor</font>，打开Vim的Tutor，一晚上练了前面四课，还是比较有效果。</p>
<p>对Tutor中常常note的一句话很有体会：<font color=#800000><strong>do not try to memorize, learn by usage</strong></font>。所以，如果你想知道怎么用Vim，装上</p>
<p>PS01：</p>
<p>1) <font color=#800000>logout</font>时，遇到<font color=#800000>There are stopped jobs</font>，才想起来自己之前不小心错误退出Vim，用<font color=#800000>job</font>看了一下，果然Vim已经stopped，<font color=#800000>fg</font>之后，正常退出Vim。</p>
<p>2) 刚开始根本没有习惯Vim的光标移动：上下左右对应kjhl，慢慢习惯了之后，在Windows下老是想用kjhl操作，才发觉使用kjhl比用方向键要方便的多，不是吗？</p>
<p>PS02：</p>
<p>1) 近11点又和GR去门口吃冷锅串串，小high一下，每天晚上整点小吃，再整上两瓶，用我在公司推广的流行语说：<font color=#800000><strong>整的好哦！</strong></font></p>
<p>2) 再补上一句：<font color=#800000><strong>明天哥还带你来！</strong></font></p>
<p>3) 后面冒出一句：<font color=#800000><strong>操你大爷！</strong></font></p>
<p>4) 早睡早起身体好！</p>
<p>本周签名：<font color=#800000><strong>谈政治就像看毛片，过瘾，但要偷偷的，多了反胃&#8230;&#8230;</strong></font></p><img src ="http://www.cppblog.com/Fox/aggbug/52203.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-06-05 00:21 <a href="http://www.cppblog.com/Fox/archive/2008/06/05/linux_vim.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>周记：找回激情</title><link>http://www.cppblog.com/Fox/archive/2008/05/11/find_enthusiasm.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Sat, 10 May 2008 18:25:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/05/11/find_enthusiasm.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/49491.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/05/11/find_enthusiasm.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/49491.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/49491.html</trackback:ping><description><![CDATA[<p>写的太杂，实在没法写题目，就用这一周的签名吧，很合现在的心境。
<p><a href="http://www.cppblog.com/kevinlynx/" target=_blank>Kevin</a>眼中的我，大概是个<font color=#800000>重视理论算法胜过编程实践</font>的人，而我的算法和理论基础尚差的出奇（可能这就是知耻而后勇吧:D），可见我的编程实践又会多么的差了。<a href="http://www.cppblog.com/Bugs/" target=_blank>Bugs</a>更是对我整日沉浸于这些不着边际的&#8220;<font color=#800000>空中楼阁</font>&#8221;颇有微词，甚至嗤之以鼻。今日若不是要把自己前段时间的豆腐渣粉饰一番，我依然不愿去考虑多线程的具体实现，或者说不是不愿，是不敢，总有一种<font color=#800000>临深履薄</font>之感。
<p>纵然如此，为了更好的完成工作，我还是拉来Kevin，劳他为我讲解一下<a href="http://en.wikipedia.org/wiki/Multithread" target=_blank>多线程</a>，可能是因为我从未仔细看过<a href="http://en.wikipedia.org/wiki/Boost_C%2B%2B_Libraries" target=_blank>boost</a>等C++开源库的原因吧，我对于结构封装本身并没有多少概念。说句实话，看到那些模板我就头大，心里想：本来一个简单的东西，为什么要搞的那么复杂呢？当然，我知道，这是因为我对其<font color=#800000>缺乏了解</font>，在对一样东西没有完全理解就妄测其好坏是自卑的表现:D，所以也请Kevin原谅我的无知，顺便致谢;-)。
<p>还是稍微提一下多线程的东西吧，因为这一次改动并不很大，因此只言片语难以面面俱到，也请各位TX不必较真儿。这儿只是说一下我是怎么偷懒把之前没有使用多线程的I/O部分修改成多线程I/O的，I/O的细节不再详述，而且这台机器上面因为没有VS，仅凭记忆，如果有什么差错，请帮我指出来了:-)。
<p>在项目启动后的初始化中初始化I/O线程： </p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; FONT-FAMILY: courier new; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;SomeApp::Init(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000">{&nbsp;&nbsp;<br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;some&nbsp;other&nbsp;things&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;CIOOperator::Init();&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;InitializeCriticalSection&nbsp;for&nbsp;I/O&nbsp;queue(s)</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">nIOThreadsNum;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">i&nbsp;)&nbsp;<br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CIOOperator&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pOpObj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;CIOOperator;&nbsp;<br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOpObj</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Create();&nbsp;<br></span><span style="COLOR: #008080">11</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_vecIOThreads.push_back(pOpObj);&nbsp;<br></span><span style="COLOR: #008080">12</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br></span><span style="COLOR: #008080">13</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">14</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;some&nbsp;other&nbsp;things&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">15</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">}</span></div>
<p><br>在项目退出前结束I/O线程：&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; FONT-FAMILY: courier new; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;SomeApp::Release(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)&nbsp;<br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000">{&nbsp;&nbsp;<br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;some&nbsp;other&nbsp;things&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;vector</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">CIOOperator&nbsp;</span><span style="COLOR: #000000">*&gt;</span><span style="COLOR: #000000">::iterator&nbsp;it&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;m_vecIOThreads.begin();&nbsp;<br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;;&nbsp;i</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">m_vecIOThreads.end();&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">it&nbsp;)&nbsp;<br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;it</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">End();&nbsp;<br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">it);&nbsp;<br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br></span><span style="COLOR: #008080">11</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;m_vecIOThreads.clear();<br></span><span style="COLOR: #008080">12</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">13</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;CIOOperator::Release();&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;DeleteCriticalSectionfor&nbsp;I/O&nbsp;queue(s)</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">14</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">15</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;some&nbsp;other&nbsp;things&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">16</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">}&nbsp;</span></div>
<p><br>I/O线程函数：&nbsp;&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; FONT-FAMILY: courier new; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ThreadFunc(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pArgument&nbsp;)<br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;some&nbsp;other&nbsp;things,&nbsp;like&nbsp;exception&nbsp;handling&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;(CIOOperator&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)pArgument</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Run();&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;operating&nbsp;I/O&nbsp;queue(s)&nbsp;until&nbsp;exit</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;some&nbsp;other&nbsp;things&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;_endthreadex();<br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">}</span></div>
<p>说多线程复杂，无外乎线程的<font color=#800000>退出策略</font>、<font color=#800000>同步机制</font>、<font color=#800000>调试</font>、<font color=#800000>异常处理</font>等等。多少还是需要一些知识（尤其是同步）和经验（尤其是<font color=#800000>调试</font>）。</p>
<p>再回来说一下最近比较关注的算法吧，虽然为某些人所不齿，甚至公然批评我最近比较松懈，实在令我难堪的紧。我又没有消极怠工，难道编写代码是积极，学习算法就是消极吗？乡下来的，且不必理他。当然，多少还是需要注意一下分寸吧。</p>
<p>在受到上次解决<a href="http://www.cppblog.com/Fox/archive/2008/04/20/flapjack_sortting.html">烙饼排序问题</a>的打击之后，我开始反思：自己思考和解决问题的角度怎么就那么简单和狭隘？细细想来，从本科毕业之后，几乎再没翻过算法的书，几乎再没做过算法的题，写代码只是为了糊口，只能糊口的代码自然只能以垃圾形容。意识粗糙，操作离谱，整个一下里巴人。
<p>在一番深深的自责之后，痛定思痛，痛何如哉，才感觉自己关于算法的思维空间已经局限于if-else、do-while，连穷举、分治、贪心、回溯这些以前念书时天天挂在嘴边侃侃而谈的常用算法都没有概念了，遑论动态规划、最小二乘法、线性回归等复杂一些的（非）数值算法，关于数据结构的思维空间也已经局限于vector、list、map了，再也没有回忆过stack、tree、graph这些读死书、死读书得来的所谓知识。&nbsp;
<p>可悲啊，为什么拿到一个困难一点的问题，就只知道画图、编码，而不知道组织算法呢？甚至连这个问题到底有无多项式时间解都不去考虑。然而，一提谁都知道：<font color=#800000>算法复杂性</font>——数据结构第0章就会提到的基础，真正分析起来，却是力不从心。
<p>所以，接着扫扫盲吧，实在没有必要去搞很多艰深的东西，本来想接下来就写<a href="http://en.wikipedia.org/wiki/NP_%28complexity%29" target=_blank>NP难题</a>，可是近来工作上的事情确实有些多，之前一篇已经是被<a href="http://www.cppblog.com/Alex/" target=_blank>Alex</a>（这家伙却至今未开张&#8230;&#8230;）&#8220;催出来&#8221;的了。</p>
<p>手头只有MIT英文版的《<a href="http://mitpress.mit.edu/algorithms/">Introduction to Algorithms</a>》ed.2，于是就从网上找了中文电子版，居然是上个世纪94年南京大学译的第一版。看算法的话，多半是以这本书和<a href="http://en.wikipedia.org/wiki/Wikipedia" target=_blank>Wikipedia</a>为主了。</p>
<p>PS: 另外做的一点事情，似乎和<a href="http://en.wikipedia.org/wiki/Lexical_analysis" target=_blank>词法分析</a>、<a href="http://en.wikipedia.org/wiki/Exception_handling" target=_blank>异常处理</a>等多少有些关联，内容相当琐碎，此刻不再赘述。</p>
<p>对了，和工作、学习并不那么相干的事情就是，今天和几个同事出去钓了几个小时鱼，收获嘛，保密:D。</p>
<p>近期考虑的关键词：无缝世界 网游安全 算法导论 兄弟激情</p><img src ="http://www.cppblog.com/Fox/aggbug/49491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-05-11 02:25 <a href="http://www.cppblog.com/Fox/archive/2008/05/11/find_enthusiasm.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编程之美：中国象棋将帅问题</title><link>http://www.cppblog.com/Fox/archive/2008/04/18/chinese_chess_one_param.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Thu, 17 Apr 2008 16:26:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/04/18/chinese_chess_one_param.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/47457.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/04/18/chinese_chess_one_param.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/47457.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/47457.html</trackback:ping><description><![CDATA[<p>Author: Fox</p>
<p>晚上没有加班，打游戏打到9点过，后面就又看了一道《<a href="http://www.china-pub.com/38070" target=_blank>编程之美</a>》的题目《<a href="http://book.csdn.net/bookfiles/656/10065620784.shtml" target=_blank>中国象棋将帅问题</a>》。</p>
<p><font color=#800000>题目：下过中国象棋的朋友都知道，双方的&#8220;将&#8221;和&#8220;帅&#8221;相隔遥远，并且它们不能照面。在象棋残局中，许多高手能利用这一规则走出精妙的杀招。假设棋盘上只有&#8220;将&#8221;和&#8220;帅&#8221;二子（如图1-3所示）（为了下面叙述方便，我们约定用<em>A</em>表示&#8220;将&#8221;，<em>B</em>表示&#8220;帅&#8221;）：</font></p>
<p align=center><font color=#800000><img height=243 alt=1-3副本 src="http://book.csdn.net/BookFiles/656/img/image003.jpg" width=224 border=0></font></p>
<p><font color=#800000><em>A</em>、<em>B</em>二子被限制在己方3&#215;3的格子里运动。例如,在如上的表格里，<em>A</em>被正方形{<em>d</em><sub>10</sub>, <em>f</em><sub>10</sub>, <em>d</em><sub>8</sub>, <em>f</em><sub>8</sub>}包围，而<em>B</em>被正方形{<em>d</em><sub>3</sub>, <em>f</em><sub>3</sub>, <em>d</em><sub>1</sub>, <em>f</em><sub>1</sub>}包围。每一步，<em>A</em>、<em>B</em>分别可以横向或纵向移动一格，但不能沿对角线移动。另外，<em>A</em>不能面对<em>B</em>，也就是说，<em>A</em>和<em>B</em>不能处于同一纵向直线上（比如<em>A</em>在<em>d</em><sub>10</sub>的位置，那么<em>B</em>就不能在<em>d</em><sub>1</sub>、<em>d</em><sub>2</sub>以及<em>d</em><sub>3</sub>）。</font>
<p><font color=#800000>请写出一个程序，输出<em>A</em>、<em>B</em>所有合法位置。要求在代码中只能使用一个变量。</font>
<p><font color=#800000></font>
<p>在纸上画了半天，Soft从台湾给带的长寿都让我抽完了，总算对得起这会儿工夫&#8230;&#8230;</p>
<p>我的思路大致如下：</p>
<p>1) 只能使用一个变量nNum ==&gt; 只能使用一个循环，nNum只能用来表示A、B位置的组合，nNum最大为9&#215;9-1=80；</p>
<p>2) 如何用nNum表示一个A、B位置的组合？</p>
<p>下图表示<font color=#800000>A（红色）</font>、<font color=#000080>B（蓝色）</font>所在位置：</p>
<table cellSpacing=0 cellPadding=20 width=102 align=center border=1>
    <tbody>
        <tr>
            <td vAlign=top width=51><font color=#800000>6</font></td>
            <td vAlign=top width=51><font color=#800000>7</font></td>
            <td vAlign=top width=51><font color=#800000>8</font></td>
        </tr>
        <tr>
            <td vAlign=top width=51><font color=#800000>3</font></td>
            <td vAlign=top width=51><font color=#800000>4</font></td>
            <td vAlign=top width=51><font color=#800000>5</font></td>
        </tr>
        <tr>
            <td vAlign=top width=51><font color=#800000>0</font></td>
            <td vAlign=top width=51><font color=#800000>1</font></td>
            <td vAlign=top width=51><font color=#800000>2</font></td>
        </tr>
        <tr>
            <td vAlign=top width=51><font color=#000080>6</font></td>
            <td vAlign=top width=51><font color=#000080>7</font></td>
            <td vAlign=top width=51><font color=#000080>8</font></td>
        </tr>
        <tr>
            <td vAlign=top width=51><font color=#000080>3</font></td>
            <td vAlign=top width=51><font color=#000080>4</font></td>
            <td vAlign=top width=51><font color=#000080>5</font></td>
        </tr>
        <tr>
            <td vAlign=top width=51><font color=#000080>0</font></td>
            <td vAlign=top width=51><font color=#000080>1</font></td>
            <td vAlign=top width=51><font color=#000080>2</font></td>
        </tr>
    </tbody>
</table>
<p>以<font color=#800000>nNum%9表示A的位置</font>，<font color=#000080>nNum/9表示B的位置</font>，如nNum==15，<font color=#800000>A==6</font>，<font color=#000080>B==1</font>。</p>
<p>3) 如何确定A、B位置的合法性？</p>
<p>规则都指定了，合法性的确定也就很简单了：A%3 != B%3。</p>
<p>OK，剩下的输出就很简单了，为了好看一点，这里希望直接按题目给的图表示出A、B的位置，如：&#8220;<font color=#800000>A:d10</font>, <font color=#000080>B:e3</font>&#8221;，还有颜色:D。</p>
<p>A的行号：A/3+8；</p>
<p>A的列号：A%3+d；</p>
<p>B的行号：B/3+1；</p>
<p>B的列号：B%3+d；</p>
<p>代码如下（注释掉的部分只是为了输出更&#8220;漂亮&#8221;一点）：<br></p>
<p>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; FONT-FAMILY: courier new; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">#include&nbsp;&lt;windows.h&gt;<br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">HANDLE&nbsp;hStdout;<br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">CONSOLE_SCREEN_BUFFER_INFO&nbsp;csbiInfo;<br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">WORD&nbsp;wOldColorAttrs;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;_tmain(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;argc,&nbsp;_TCHAR</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;argv[])<br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">hStdout&nbsp;=&nbsp;GetStdHandle(STD_OUTPUT_HANDLE);<br></span><span style="COLOR: #008080">11</span>&nbsp;<span style="COLOR: #008000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">GetConsoleScreenBufferInfo(hStdout,&nbsp;&amp;csbiInfo);<br></span><span style="COLOR: #008080">12</span>&nbsp;<span style="COLOR: #008000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">wOldColorAttrs&nbsp;=&nbsp;csbiInfo.wAttributes;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">13</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">14</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;nNum&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">81</span><span style="COLOR: #000000">;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;nNum表示所有位置（含非法），故nNum&nbsp;=&nbsp;3&nbsp;*&nbsp;3&nbsp;*&nbsp;3&nbsp;*&nbsp;3</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">15</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(&nbsp;nNum</span><span style="COLOR: #000000">--</span><span style="COLOR: #000000">&nbsp;)<br></span><span style="COLOR: #008080">16</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;{<br></span><span style="COLOR: #008080">17</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;nNum</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;nNum</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">&nbsp;)<br></span><span style="COLOR: #008080">18</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br></span><span style="COLOR: #008080">19</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">SetConsoleTextAttribute(hStdout,&nbsp;FOREGROUND_RED&nbsp;|&nbsp;FOREGROUND_INTENSITY);</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">20</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">A:%x%02d&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;nNum</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">0xd</span><span style="COLOR: #000000">,&nbsp;nNum</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">8</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">21</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">SetConsoleTextAttribute(hStdout,&nbsp;FOREGROUND_BLUE&nbsp;|&nbsp;FOREGROUND_INTENSITY);</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">22</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">B:%x%02d&nbsp;&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;nNum</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">0xd</span><span style="COLOR: #000000">,&nbsp;nNum</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">23</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #008080">24</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(nNum</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">)&nbsp;)<br></span><span style="COLOR: #008080">25</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">26</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;};<br></span><span style="COLOR: #008080">27</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">28</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">SetConsoleTextAttribute(hStdout,&nbsp;wOldColorAttrs);</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">29</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">30</span>&nbsp;<span style="COLOR: #000000">}</span></div>
<p>输出：</p>
<p align=center><a href=http://picasaweb.google.com/yulefox/Blog/photo#5190249214067771810 target=_blank><img src="http://lh3.ggpht.com/yulefox/SAd4KTxCiaI/AAAAAAAAAK8/WyHyft6R-QY/s800/one_param_output.JPG">点击查看更清晰原图:D</a> </p>
<p>PS: 刚写完，没有来得及总结更多，急着向<a href="http://pekingone.blog.sohu.com/" target=_blank>LP</a>炫耀。但上面的思路应该比较清晰了，也不管书上的答案了，反正我感觉我这点代码效率应该也不会低到哪儿吧:-)？</p><img src ="http://www.cppblog.com/Fox/aggbug/47457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-04-18 00:26 <a href="http://www.cppblog.com/Fox/archive/2008/04/18/chinese_chess_one_param.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编程之美：让CPU占用率曲线听你指挥</title><link>http://www.cppblog.com/Fox/archive/2008/04/17/control_cpu_using_curve.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 16 Apr 2008 16:20:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/04/17/control_cpu_using_curve.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/47343.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/04/17/control_cpu_using_curve.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/47343.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/47343.html</trackback:ping><description><![CDATA[<p>Author: Fox</p> <p>前两天在买《<a href="http://en.wikipedia.org/wiki/The_Art_of_Computer_Programming" target="_blank">计算机程序设计艺术</a>》中文版的时候，偶然发现《<a href="http://www.china-pub.com/38070" target="_blank">编程之美</a>》这本书，当时翻了一下，看到“<font color="#800000">让CPU占用率曲线听你指挥</font>”这样的题目确实让人为之一动。写一段代码，可以让CPU占有率曲线画出平滑的正弦曲线，有点意思:-)。</p> <p>当然，最后没有买这本书，虽然我可以肯定这是本好书。</p> <p>我从<a href="http://book.csdn.net/" target="_blank">CSDN读书</a>上找到几节，闲来读一读。今天来讨论一下《<a href="http://book.csdn.net/bookfiles/656/10065620783.shtml" target="_blank">让CPU占用率曲线听你指挥</a>》。</p> <p><font color="#800000">题目：写一个程序，让用户来决定Windows任务管理器（Task Manager）的CPU占用率。程序越精简越好，计算机语言不限。例如，可以实现下面三种情况：</font></p> <p><font color="#800000">1.&nbsp;&nbsp;&nbsp; CPU的占用率固定在50%，为一条直线；</font> <p><font color="#800000">2.&nbsp;&nbsp;&nbsp; CPU的占用率为一条直线，但是具体占用率由命令行参数决定（参数范围1~ 100）；</font> <p><font color="#800000">3.&nbsp;&nbsp;&nbsp; CPU的占用率状态是一个正弦曲线。</font> <p>在讨论具体实现之前，一个非常重要的问题是：<font color="#800000">什么是CPU占用率</font>？</p> <p>《<a href="http://www.china-pub.com/38070" target="_blank">编程之美</a>》写道：“<font color="#800000">在任务管理器的一个刷新周期内，CPU忙（执行应用程序）的时间和刷新周期总时间的比率，就是CPU的占用率，也就是说，任务管理器中显示的是每个刷新周期内CPU占用率的统计平均值。</font>”</p> <p>打开“Windows 任务管理器”，“性能”中有“CPU使用记录”一项，给出的就是CPU占有率曲线。</p> <p>至于一个刷新周期到底是多长，<a href="http://book.csdn.net/bookfiles/656/10065620783.shtml" target="_blank">书中</a>似乎没有明确给出，只是说“大约是1秒钟更新一次”，我打开Windows自带的时钟，也感觉大约是1秒钟。</p> <p>另外的常识是：</p> <p>单核环境下，空死循环会导致100%的CPU占有率。双核环境下，CPU总占有率大约为50%，四核会不会是25%左右呢？（我没有四核，只能猜测了，估计各核间切换也会耗掉点时间，因为我的双核环境并没有出现一核100%，另一核空闲的情况）。</p> <p>当CPU整个刷新周期（绝大多数时间）空闲时，CPU占有率趋于0。</p> <p><a href="http://book.csdn.net/bookfiles/656/10065620783.shtml" target="_blank">书中</a>给出的正弦实现如下：</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; font-family: courier new; background-color: #eeeeee"><span style="color: #008080">1</span>&nbsp;<span style="color: #000000">#include </span><span style="color: #000000">"</span><span style="color: #000000">Windows.h</span><span style="color: #000000">"</span><span style="color: #000000"><br></span><span style="color: #008080">2</span>&nbsp;<span style="color: #000000">#include </span><span style="color: #000000">"</span><span style="color: #000000">stdlib.h</span><span style="color: #000000">"</span><span style="color: #000000"><br></span><span style="color: #008080">3</span>&nbsp;<span style="color: #000000">#include </span><span style="color: #000000">"</span><span style="color: #000000">math.h</span><span style="color: #000000">"</span><span style="color: #000000">&nbsp;<br></span><span style="color: #008080">4</span>&nbsp;<span style="color: #000000"><br></span><span style="color: #008080">5</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">double</span><span style="color: #000000"> SPLIT </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0.01</span><span style="color: #000000">;<br></span><span style="color: #008080">6</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000"> COUNT </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">200</span><span style="color: #000000">;<br></span><span style="color: #008080">7</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">double</span><span style="color: #000000"> PI </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">3.14159265</span><span style="color: #000000">;<br></span><span style="color: #008080">8</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000"> INTERVAL </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">300</span><span style="color: #000000">; <br></span><span style="color: #008080">9</span>&nbsp;<span style="color: #000000"><br></span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">int</span><span style="color: #000000"> _tmain(</span><span style="color: #0000ff">int</span><span style="color: #000000"> argc, _TCHAR</span><span style="color: #000000">*</span><span style="color: #000000"> argv[])<br></span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">{<br></span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; DWORD busySpan[COUNT];&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000">array of busy times</span><span style="color: #008000"><br></span><span style="color: #008080">13</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">&nbsp;&nbsp;&nbsp; DWORD idleSpan[COUNT];&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000">array of idle times</span><span style="color: #008000"><br></span><span style="color: #008080">14</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">int</span><span style="color: #000000"> half </span><span style="color: #000000">=</span><span style="color: #000000"> INTERVAL </span><span style="color: #000000">/</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">2</span><span style="color: #000000">;<br></span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">double</span><span style="color: #000000"> radian </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0.0</span><span style="color: #000000">;<br></span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">for</span><span style="color: #000000">(</span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> COUNT; i</span><span style="color: #000000">++</span><span style="color: #000000">)<br></span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; {<br></span><span style="color: #008080">18</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; busySpan[i] </span><span style="color: #000000">=</span><span style="color: #000000"> (DWORD)(half </span><span style="color: #000000">+</span><span style="color: #000000"> (sin(PI </span><span style="color: #000000">*</span><span style="color: #000000"> radian) </span><span style="color: #000000">*</span><span style="color: #000000"> half));<br></span><span style="color: #008080">19</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; idleSpan[i] </span><span style="color: #000000">=</span><span style="color: #000000"> INTERVAL </span><span style="color: #000000">-</span><span style="color: #000000"> busySpan[i];<br></span><span style="color: #008080">20</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; radian </span><span style="color: #000000">+=</span><span style="color: #000000"> SPLIT;<br></span><span style="color: #008080">21</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; }<br></span><span style="color: #008080">22</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; DWORD startTime </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br></span><span style="color: #008080">23</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">int</span><span style="color: #000000"> j </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br></span><span style="color: #008080">24</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">while</span><span style="color: #000000"> (</span><span style="color: #0000ff">true</span><span style="color: #000000">)<br></span><span style="color: #008080">25</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; {<br></span><span style="color: #008080">26</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j </span><span style="color: #000000">=</span><span style="color: #000000"> j </span><span style="color: #000000">%</span><span style="color: #000000"> COUNT;<br></span><span style="color: #008080">27</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startTime </span><span style="color: #000000">=</span><span style="color: #000000"> GetTickCount();<br></span><span style="color: #008080">28</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">while</span><span style="color: #000000"> ((GetTickCount() </span><span style="color: #000000">-</span><span style="color: #000000"> startTime) </span><span style="color: #000000">&lt;=</span><span style="color: #000000"> busySpan[j]) ;<br></span><span style="color: #008080">29</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep(idleSpan[j]);<br></span><span style="color: #008080">30</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j</span><span style="color: #000000">++</span><span style="color: #000000">;<br></span><span style="color: #008080">31</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; }<br></span><span style="color: #008080">32</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br></span><span style="color: #008080">33</span>&nbsp;<span style="color: #000000">}</span></div> <p><br>在单核环境（P4 2.40）下，其表现还是不错的：</p> <p align="center"><a href="http://picasaweb.google.com/yulefox/Blog/photo#5189783656792754530"><img src="http://lh5.ggpht.com/yulefox/SAXQvTxCiWI/AAAAAAAAAI4/pG3RTW7cK48/s144/cpu_curve_sin.jpg">点击查看大图</a></p> <p>在双核环境（Core2 E4500）下，就有点<strike>差强人意</strike>不尽人意了：</p> <p align="center"><a href="http://picasaweb.google.com/yulefox/Blog/photo#5189783669677656434"><img src="http://lh4.ggpht.com/yulefox/SAXQwDxCiXI/AAAAAAAAAJA/uAcw6fG2HsA/s144/cpu_curve_sin_core2.jpg">点击查看大图</a></p> <p>不过，总还能看出是正弦曲线。</p> <p>上面两图的问题：</p> <p>1) 单核时曲线不够平滑，是由于QQ等程序占用CPU所致；</p> <p>2) 双核时曲线更加抖动，我的理解是除其他程序影响外，由于线程没有固定运行在一个CPU上导致的，后面看到<a href="http://book.csdn.net/bookfiles/656/10065620783.shtml" target="_blank">书上</a>提到<font color="#800000">线程迁移</font>，个人感觉这个叫法欠妥啊，总觉得<font color="#800000">线程迁移</font>令人费解。</p> <p>可以立即想到的是：<font color="#800000">让进程在指定处理器上运行（处理器亲缘关系）</font>，由Windows提供了两个API可以做到这一点：<strong>GetCurrentProcess</strong>和<strong>SetProcessAffinityMask</strong>的。</p> <p>修改之后的代码如下：</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; font-family: courier new; background-color: #eeeeee"><span style="color: #008080">1</span>&nbsp;<span style="color: #000000">#include </span><span style="color: #000000">"</span><span style="color: #000000">Windows.h</span><span style="color: #000000">"</span><span style="color: #000000"><br></span><span style="color: #008080">2</span>&nbsp;<span style="color: #000000">#include </span><span style="color: #000000">"</span><span style="color: #000000">stdlib.h</span><span style="color: #000000">"</span><span style="color: #000000"><br></span><span style="color: #008080">3</span>&nbsp;<span style="color: #000000">#include </span><span style="color: #000000">"</span><span style="color: #000000">math.h</span><span style="color: #000000">"</span><span style="color: #000000">&nbsp;<br></span><span style="color: #008080">4</span>&nbsp;<span style="color: #000000"><br></span><span style="color: #008080">5</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">double</span><span style="color: #000000"> SPLIT </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0.01</span><span style="color: #000000">;<br></span><span style="color: #008080">6</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000"> COUNT </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">200</span><span style="color: #000000">;<br></span><span style="color: #008080">7</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">double</span><span style="color: #000000"> PI </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">3.14159265</span><span style="color: #000000">;<br></span><span style="color: #008080">8</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000"> INTERVAL </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">300</span><span style="color: #000000">; <br></span><span style="color: #008080">9</span>&nbsp;<span style="color: #000000"><br></span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000"></span><span style="color: #0000ff">int</span><span style="color: #000000"> _tmain(</span><span style="color: #0000ff">int</span><span style="color: #000000"> argc, _TCHAR</span><span style="color: #000000">*</span><span style="color: #000000"> argv[])<br></span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">{<br></span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp; <span style="color: red">SetProcessAffinityMask</span>(<br></span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red">GetCurrentProcess</span>(),<br></span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: red">0x00000001&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #008000">//cpu mask</span></span><span style="color: #000000"><br></span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ); <br></span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000"><br></span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; DWORD busySpan[COUNT];&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000">array of busy times</span><span style="color: #008000"><br></span><span style="color: #008080">18</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">&nbsp;&nbsp;&nbsp; DWORD idleSpan[COUNT];&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000">array of idle times</span><span style="color: #008000"><br></span><span style="color: #008080">19</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">int</span><span style="color: #000000"> half </span><span style="color: #000000">=</span><span style="color: #000000"> INTERVAL </span><span style="color: #000000">/</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">2</span><span style="color: #000000">;<br></span><span style="color: #008080">20</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">double</span><span style="color: #000000"> radian </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0.0</span><span style="color: #000000">;<br></span><span style="color: #008080">21</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">for</span><span style="color: #000000">(</span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> COUNT; i</span><span style="color: #000000">++</span><span style="color: #000000">)<br></span><span style="color: #008080">22</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; {<br></span><span style="color: #008080">23</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; busySpan[i] </span><span style="color: #000000">=</span><span style="color: #000000"> (DWORD)(half </span><span style="color: #000000">+</span><span style="color: #000000"> (sin(PI </span><span style="color: #000000">*</span><span style="color: #000000"> radian) </span><span style="color: #000000">*</span><span style="color: #000000"> half));<br></span><span style="color: #008080">24</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; idleSpan[i] </span><span style="color: #000000">=</span><span style="color: #000000"> INTERVAL </span><span style="color: #000000">-</span><span style="color: #000000"> busySpan[i];<br></span><span style="color: #008080">25</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; radian </span><span style="color: #000000">+=</span><span style="color: #000000"> SPLIT;<br></span><span style="color: #008080">26</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; }<br></span><span style="color: #008080">27</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; DWORD startTime </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br></span><span style="color: #008080">28</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">int</span><span style="color: #000000"> j </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br></span><span style="color: #008080">29</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">while</span><span style="color: #000000"> (</span><span style="color: #0000ff">true</span><span style="color: #000000">)<br></span><span style="color: #008080">30</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; {<br></span><span style="color: #008080">31</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j </span><span style="color: #000000">=</span><span style="color: #000000"> j </span><span style="color: #000000">%</span><span style="color: #000000"> COUNT;<br></span><span style="color: #008080">32</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startTime </span><span style="color: #000000">=</span><span style="color: #000000"> GetTickCount();<br></span><span style="color: #008080">33</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">while</span><span style="color: #000000"> ((GetTickCount() </span><span style="color: #000000">-</span><span style="color: #000000"> startTime) </span><span style="color: #000000">&lt;=</span><span style="color: #000000"> busySpan[j]) ;<br></span><span style="color: #008080">34</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sleep(idleSpan[j]);<br></span><span style="color: #008080">35</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j</span><span style="color: #000000">++</span><span style="color: #000000">;<br></span><span style="color: #008080">36</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; }<br></span><span style="color: #008080">37</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br></span><span style="color: #008080">38</span>&nbsp;<span style="color: #000000">}</span></div> <p><br>双核环境（Core2 E4500）修改之后的输出如下：</p> <p align="center"><a href="http://picasaweb.google.com/yulefox/Blog/photo#5189877484648302978"><img src="http://lh4.ggpht.com/yulefox/SAYmEzxCiYI/AAAAAAAAAJ4/822JVBb5o3s/s144/cpu_curve_sin_core2_cpu1.jpg">点击查看大图</a></p> <p>我理想中的表现是：</p> <p><font color="#800000">1) 曲线是平滑的，最好不因其他应用程序或操作的执行而改变；</font></p> <p><font color="#800000">2) 不管是单核还是双核，峰值皆为100%，谷值为0。</font></p> <p>对于第一点，其实就是保证任一刷新周期中的CPU占有率都可以被精确控制在0-100之间，如果你可以使CPU一直保持50%（而不是近似的上下波动），产生一条平滑的曲线就很easy了。</p> <p>问题的关键在于，除了当前你写的程序可以控制，其他程序或操作如何控制？或者说：<font color="#800000">如何控制CPU的运行情况才是关键之处</font>。</p> <p>PS: 一晚上老是断网，搞得思路频频被打断，兴致也损了大半。总之，《<a href="http://www.china-pub.com/38070" target="_blank">编程之美</a>》还是值得玩味一把吧:D。</p><img src ="http://www.cppblog.com/Fox/aggbug/47343.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-04-17 00:20 <a href="http://www.cppblog.com/Fox/archive/2008/04/17/control_cpu_using_curve.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何阅读、使用Blog?</title><link>http://www.cppblog.com/Fox/archive/2008/04/15/read_use_blog.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 15 Apr 2008 07:59:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/04/15/read_use_blog.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/47131.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/04/15/read_use_blog.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/47131.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/47131.html</trackback:ping><description><![CDATA[<p>Author: Fox</p> <p>本文写给满足以下条件的你（前面4点对应只读的你，后面4点对应只写的你)：</p> <p>1) 经常阅读别人的Blog，所谓经常，平均每天阅读量在100篇左右；</p> <p>2) 不希望花费大量时间在输入网址、鼠标点击和滚动上；</p> <p>3) 有固定的阅读习惯，指专注于某一领域、某些特定的Blog；</p> <p>4) 尚未使用或刚使用<a href="http://www.google.com/reader" target="_blank">Google Reader</a>并愿意使用<a href="http://www.google.com/reader" target="_blank">Google Reader</a>；</p> <p>5) 经常更新自己的Blog，所谓经常，平均每月更新1篇或以上；</p> <p>2) 不希望花费大量时间在复制、粘贴上；</p> <p>3) 希望多与其他人交流；</p> <p>4) 尚未使用或刚使用<a href="http://get.live.com/writer/overview" target="_blank">Windows Live Writer</a>并愿意使用<a href="http://get.live.com/writer/overview" target="_blank">Windows Live Writer</a>。</p> <p>1、For只读的你</p> <p>1) 启用<a href="http://www.google.com/reader" target="_blank">Google Reader</a></p> <p>做互联网的就是做互联网的，<a href="http://www.google.com/" target="_blank">Google</a>的<a href="http://www.google.com/reader" target="_blank">Google Reader</a>有个帐号就能开启。</p> <p>之前写过一个<a href="http://www.cppblog.com/Fox/articles/tutorial_for_gf.html" target="_blank">给我GF看的一点东西</a>，这儿对于启用<a href="http://www.google.com/reader" target="_blank">Google Reader</a>不再赘述了，有需要的TX可以看一下或者直接<a href="http://www.google.com/" target="_blank">Google</a>；</p> <p>2) 添加<a title="什么是RSS地址" href="http://baike.baidu.com/view/1644.htm" target="_blank">RSS地址</a></p> <p>首先要找到<a href="http://baike.baidu.com/view/1644.htm" target="_blank">RSS地址</a>，大多数网站提供的Blog，<a href="http://baike.baidu.com/view/1644.htm" target="_blank">RSS地址</a>都摆在显眼的地方，对于QQ空间这种算不上Blog的Blog来说，QQ空间的RSS也存在，比如，QQ号码为10000的用户，它的RSS就是：<a href="http://feeds.qzone.qq.com/cgi-bin/cgi_rss_out?uin=10000" target="_blank">http://feeds.qzone.qq.com/cgi-bin/cgi_rss_out?uin=10000</a>。</p> <p>找到<a href="http://baike.baidu.com/view/1644.htm" target="_blank">RSS地址</a>之后，就可以将其添加到订阅列表里面了。</p> <p>3) 关于<a title="什么是OPML" href="http://baike.baidu.com/view/127329.htm" target="_blank">OPML</a></p> <p>如果你想共享或备份你的订阅，<a href="http://www.google.com/reader" target="_blank">Google Reader</a>具有“导入/导出”功能，不提供具体的使用方式了，<a href="http://www.google.com/" target="_blank">Google</a>吧。</p> <p>4) 星标和标签</p> <p>看过的好文希望下次再读，就加个星标吧。</p> <p>RSS太多，就使用标签吧。</p> <p>觉得我这儿写的太简单，就<a href="http://www.google.com/" target="_blank">Google</a>吧，或者看看<a href="http://www.williamlong.info/archives/655.html" target="_blank">Google Reader的四个常用小技巧</a>。</p> <p>2、For只写的你</p> <p>1) 使用<a href="http://get.live.com/writer/overview" target="_blank">Windows Live Writer</a></p> <p>做软件的就是做软件的，Microsoft的这个<a href="http://get.live.com/writer/overview" target="_blank">Windows Live Writer</a>是要下载安装的。</p> <p>具体如何去用，下载下来自然就会用了。</p> <p>友情提示：<a href="http://get.live.com/writer/overview" target="_blank">Live Writer</a>的格式、超链接、查看、草稿、帐户、日志、分类列表，都很好用。</p> <p>2) 关于<a title="什么是CSS" href="http://baike.baidu.com/view/15916.htm" target="_blank">CSS</a></p> <p>我不太喜欢Blog的页面太乱，一会儿五号字，一会儿二号字，一会儿宋体，一会儿楷体。</p> <p>我喜欢把一些我认为<font color="#800000">重要的地方</font>和<a href="http://www.cppblog.com/fox/" target="_blank">链接的内容</a>突出显示，以前没添加<a title="什么是CSS" href="http://baike.baidu.com/view/15916.htm" target="_blank">CSS</a>的时候，每次都要自己去一个一个的修改链接的字体和颜色，浪费很多时间，如果你的Blog支持<a title="什么是CSS" href="http://baike.baidu.com/view/15916.htm" target="_blank">CSS</a>，就去看一下怎么使用吧，比如我的<a title="什么是CSS" href="http://baike.baidu.com/view/15916.htm" target="_blank">CSS</a>就很简单：</p> <p>&lt;style type=text/css&gt;<br>#top a{ border-bottom:1px dashed; color:white;&nbsp; }<br>#top a:link{ border-bottom:1px dashed; color:white; }<br>#top a:hover{ border-bottom:1px dashed; color:white; }<br>#top a:visited{ border-bottom:1px dashed; color:white; }<br>.post a:link{ border-bottom:1px dashed; color:maroon; }<br>.post a:hover{ border-bottom:1px dashed; color:maroon; }<br>.post a:visited{ border-bottom:1px dashed; color:maroon; }<br>.postbody a{ color:white; background:maroon;&nbsp; }<br>.postbody a:link{ color:white; background:maroon;&nbsp; }<br>.postbody a:hover{ color:white; background:maroon; }<br>.postbody a:visited{ color:white; background:maroon; }<br>&lt;/style&gt;  <p>我还使用了<a href="http://www.google.cn/search?complete=1&amp;hl=zh-CN&amp;newwindow=1&amp;q=devtoolbar&amp;meta=&amp;aq=0&amp;oq=devtoo" target="_blank">DevToolBar</a>帮助我确定了CSS。</p> <p>3) 添加Google专用的订阅</p> <p>我为此还专门PS了一张图片，你可以将下面的代码放到“<font color="#800000">公告</font>”里面，当然，你想放到哪儿就放到哪儿：</p> <p>&lt;br /&gt;订阅到：&lt;br /&gt;<br>&lt;a href="<a href="http://fusion.google.com/add?source=atgs&amp;feedurl=http%3A//www.cppblog.com/Fox/Rss.aspx&quot;">http://fusion.google.com/add?source=atgs&amp;feedurl=http%3A//www.cppblog.com/Fox/Rss.aspx"</a>&gt;&lt;img src="<a href="http://www.cppblog.com/images/cppblog_com/Fox/6064/o_GoogleRss.jpg&quot;">http://www.cppblog.com/images/cppblog_com/Fox/6064/o_GoogleRss.jpg"</a> border="0" alt="添加 游戏人生 到 Google 阅读器"&gt;&lt;/a&gt;&lt;br /&gt;  <p>我这儿是有图片的，只有文字的就是这样：</p> <p>&lt;br /&gt;订阅到：&lt;br /&gt;<br>&lt;a href="<a href="http://fusion.google.com/add?source=atgs&amp;feedurl=http%3A//www.cppblog.com/Fox/Rss.aspx">http://fusion.google.com/add?source=atgs&amp;feedurl=http%3A//www.cppblog.com/Fox/Rss.aspx"</a>&gt;添加 游戏人生 到 Google 阅读器&lt;/a&gt;&lt;br /&gt;  <p>上面的<a href="http://www.cppblog.com/Fox/Rss.aspx">www.cppblog.com/Fox/Rss.aspx</a>是我的<a title="什么是RSS地址" href="http://baike.baidu.com/view/1644.htm" target="_blank">RSS地址</a>，你要换成你的:-)。  <p>4) 关于邮箱地址  <p><font color="#800000">不要把邮箱地址直接放在页面上</font>，我之前曾经这样做，后面每天收到不少的垃圾邮件，就取消了，因为这年头，写个邮箱地址搜索器，然后发垃圾邮件太easy了。  <p>PS: 好用的东西还很多，因为我自己是GFans，所有在工作、学习和生活中使用了Analytics、Gmail、iGoogle、Picasa、Reader、Talk、笔记本、快讯、日历、网页历史记录、网站管理员工具、文件，反正都是免费的，而且用来比较方便，一个帐号可以搞定很多东西:D。</p><img src ="http://www.cppblog.com/Fox/aggbug/47131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-04-15 15:59 <a href="http://www.cppblog.com/Fox/archive/2008/04/15/read_use_blog.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转贴：成都锦天科技（隶属上海盛大网络） 招聘信息！ </title><link>http://www.cppblog.com/Fox/archive/2008/04/11/invite_job.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Fri, 11 Apr 2008 04:41:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/04/11/invite_job.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/46825.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/04/11/invite_job.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/46825.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/46825.html</trackback:ping><description><![CDATA[<p>转自：<a href="/Bugs/archive/2008/04/01/45903.html">http://www.cppblog.com/Bugs/archive/2008/04/01/45903.html</a></p>
		<p>基本要求：<br>1、有软件工程思想,熟悉面向对象思想。<br>2、有良好的编码风格和文档编写习惯<br>3、熟悉C++语言,了解STL<br>4、了解多线程程序设计技术<br>5、热爱游戏、有游戏开发经验者优先<br>6、有团队开发精神优先<br><br>客户端程序员：<br>1、了解DirectX编程技术，有良好的数学、各种算法基础优先<br>2、有图形开发经验者优先<br>3、熟悉UI编程技术优先<br>4、熟悉引擎开发者优先<br><br>服务器端程序员：<br>1、熟悉通信以及网络安全技术. 熟悉TCP/IP协议及相关编程技术优先<br>2、有关系数据库编程及概念经验（MySql、PostgreSQL、MS Sql）<br>3、了解分布式服务器架构技术优先<br>4、了解Lua、Python有游戏脚本系统设计经验优先<br><br>办公地点：成都<br>有意向的朋友跟贴或发简历到本博左侧邮箱。</p><img src ="http://www.cppblog.com/Fox/aggbug/46825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-04-11 12:41 <a href="http://www.cppblog.com/Fox/archive/2008/04/11/invite_job.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>整理一下，准备归位</title><link>http://www.cppblog.com/Fox/archive/2008/02/29/trivial_thing_before_return.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Fri, 29 Feb 2008 10:11:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/02/29/trivial_thing_before_return.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/43480.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/02/29/trivial_thing_before_return.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/43480.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/43480.html</trackback:ping><description><![CDATA[<p>Author: Fox </p>
		<p>
				<span style="COLOR: green">//-----------------------------------------------------------------------------------------------------<br />因为要归位回原公司了，手上便没有什么工作了，难得清闲下来，就整理一些很杂的东西，准备下周回去了<span style="FONT-FAMILY: Wingdings">J</span>。<br />//----------------------------------------------------------------------------------------------------- </span>
		</p>
		<p>零、关于此文 </p>
		<p>下面的东西很杂，但并非没有关联。 </p>
		<p>一、关于《疯狂的程序员》 </p>
		<p>有些搞程序的人有个癖性：自负偏激，主观绝对。<span style="COLOR: green"></span></p>
		<p>早就看过一点儿，因为时间，没有怎么关注，下午就找来CSDN的blog上面连载的<a href="http://blog.csdn.net/hitetoshi">《疯狂的程序员》</a>来看。<span style="COLOR: green"></span></p>
		<p>正如很多人所评论的，在绝影身上，大家都找到了自己当初学程序的影子。哪个稍有点技术的程序没有点自负的心理呢？绝影的性格很偏激，人很要面子，想法很阴暗，对世界的认识很主观、很绝对。 </p>
		<p>可惜的是，这个东西，只能当作小说看，其他不做评论…… </p>
		<p>二、关于Windows的环境变量 </p>
		<p>有些搞程序的人有个癖性：能用键盘的坚决不用鼠标。 </p>
		<p>查看帮助，按F1，重命名，按F2，搜索，按F3，输入地址，按F4，刷新，按F5…… </p>
		<p>桌面上很干净，没有"我的电脑"的图标，都用WinKey+E。没有IE的图标，都用WinKey+R，输入iexplore，回车…… </p>
		<p>快速启动里面没有"显示桌面"的图标，都用WinKey+D，或者WinKey+M，没有Outlook的图标，都用WinKey+R，输入outlook，回车，Word就输入winword，记事本就输入notepad…… </p>
		<p>打开大多程序，不用鼠标到处点击，都是"运行"里面输入命令行替代了。除了Windows默认的这些，自己常用的其他软件怎么办呢？Windows有个环境变量。下面我给大家show一下，怎么不用鼠标，完成自定义环境变量，实现命令打开任一软件<span style="FONT-FAMILY: Wingdings">J</span>。 </p>
		<p>1、WinKey+E，打开"我的电脑"； </p>
		<p>2、Alt+Enter，打开其"属性"； </p>
		<p>3、方向键，选择"高级"； </p>
		<p>4、Alt+N，打开"环境变量"； </p>
		<p>5、Tab键，选择"系统变量"； </p>
		<p>6、方向键，选择"Path"； </p>
		<p>7、Alt+I，打开"编辑"； </p>
		<p>8、End键，移动到最后； </p>
		<p>9、输入";targetpath"；* </p>
		<p>10、Tab键，选择"确定"，回车，Tab键，选择"确定"，回车； </p>
		<p>11、WinKey+R，打开"运行"； </p>
		<p>12、输入"softname"，回车；* </p>
		<p>13、Over。 </p>
		<p>
				<span style="COLOR: green">//-----------------------------------------------------------------------------------------------------<br />其中，targetpath为soft所在文件夹绝对路径，softname为软件对应的exe文件名（.exe可省略）……<br />//----------------------------------------------------------------------------------------------------- </span>
		</p><img src ="http://www.cppblog.com/Fox/aggbug/43480.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Fox/" target="_blank">Fox</a> 2008-02-29 18:11 <a href="http://www.cppblog.com/Fox/archive/2008/02/29/trivial_thing_before_return.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>