﻿<?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>游戏人生 != ( 人生 == 游戏 ) &lt;br /&gt;
站点迁移至：&lt;a href="http://www.yulefox.com"&gt;http://www.yulefox.com&lt;/a&gt;。请订阅本博的朋友将RSS修改为&lt;a href="http://feeds.feedburner.com/yulefox"&gt;http://feeds.feedburner.com/yulefox&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 06 Jan 2010 21:30:37 GMT</lastBuildDate><pubDate>Wed, 06 Jan 2010 21:30:37 GMT</pubDate><ttl>60</ttl><item><title>automake使用小记</title><link>http://www.cppblog.com/Fox/archive/2010/01/07/105053.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 06 Jan 2010 17:32:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2010/01/07/105053.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/105053.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2010/01/07/105053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/105053.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/105053.html</trackback:ping><description><![CDATA[<p>本文同步自<a mce_href="http://www.yulefox.com/"  href="http://www.yulefox.com/" target="_blank">游戏人生</a><br></p>
<p>最近有点忙，本来要用autoconf+automake把自己的代码梳理一下的，因为工作停了近两周。</p>
<p>本想看看有什么工具可以自动生成Makefile.am，答案是：<a href="http://www.gnu.org/software/automake/manual/html_node/wildcards.html" mce_href="http://www.gnu.org/software/automake/manual/html_node/wildcards.html" target="_blank">Automake不支持通配符，而且还口口声声，振振有词</a>。既然说的这么言词凿凿，情深意切，我想我也没有必要用shell生成Makefile.am了。</p>
<p>用着用着，我有点怀疑人生了：不知道什么时候需要用autoconf和automake。如果我只是平时自己写一些toy
codes的话，感觉用autoconf和automake有点大炮打蚊子的感觉，而且每次新加代码或者是移除代码、甚至是更改目录，都要重新执行
autoconf、automake（不知道我说的对与不对）。对于一个大型项目，执行一次configure和make是很痛苦的一件事，make的中
间目标文件或者库文件、执行文件倒是不一定非得完全rebuild，configure的配置检查呢？是不是也有类似机制？反正我在用ogre或者
cegui的时候，每次执行./configure是重新配置了的。</p>
<p>实际在开源项目里面也不可能维护两套makefile吧。</p>
<p>看了一下<a href="http://code.google.com/p/googletest/" mce_href="http://code.google.com/p/googletest/" target="_blank">googletest</a>的配置，倒是清爽的很，最大的特点是只有一个Makefile.am，这样在一个项目里面只需要维护一个Makefile.am就够了。</p>
<p><a href="http://crayzedsgui.svn.sourceforge.net/viewvc/crayzedsgui/cegui_mk2/trunk/" mce_href="http://crayzedsgui.svn.sourceforge.net/viewvc/crayzedsgui/cegui_mk2/trunk/" target="_blank">cegui</a>比较常规，每个子目录都会维护一个Makefile.am。</p>
<p>需要特别注意的是ogre从<a href="https://svn.ogre3d.org/svnroot/ogre/" mce_href="https://svn.ogre3d.org/svnroot/ogre/" target="_blank">1.7.0</a>开始已经开始使用<a href="http://www.cmake.org/" mce_href="http://www.cmake.org/" target="_blank">cmake</a>了&#8230;&#8230;</p>
<p>请听题：管理中小型项目，你倾向于下面哪个工具？</p>
<blockquote>
<p>o make：钻木取火，玩的就是个技术，编译代码，只用装B的，不用牛B的，你要是用什么cmake，你都不好意思跟别人打招呼，这么经典的东西，精通需要多久？要我说怎么着也得个把俩月吧，个把俩月？那是入门，至少半年，就这还得有Feldman的悟性，不舍昼夜；</p>
<p>o
autoconf+automake：既有群众基础，又有技术含量，你是那样拉轰的男人，不管在什么地方，就好像漆黑中的萤火虫一样，那样的鲜明，那样的
出众。你那忧郁的眼神，稀嘘的胡喳子，神乎其技的指法；既可以耻笑原始人的生产力低下，还可以鄙视现代人的不学无术。</p>
<p>o
cmake：在MSVCers面前抬不起头，在UNIXers面前似乎更抬不起头；而cmake对WINDOWS和UNIX平台的完美支持，足以让所有的
MSVCers和UNIXer在你面前抬不起头，你是公鸡中的战斗机。所以你还是可以趾高气昂的丢下一句：走NB的路，让SB说去吧。</p>
</blockquote>
<br><img src ="http://www.cppblog.com/Fox/aggbug/105053.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> 2010-01-07 01:32 <a href="http://www.cppblog.com/Fox/archive/2010/01/07/105053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Autotools初体验</title><link>http://www.cppblog.com/Fox/archive/2009/12/23/autotools_first_use.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 22 Dec 2009 18:18:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/12/23/autotools_first_use.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/103743.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/12/23/autotools_first_use.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/103743.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/103743.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 从接触和使用make以来，前前后后写了不少Makefile（添添减减、修修补补，累计上千行是有的），今天在重新整理代码的组织结构之后，突然就想：我为什么不使用Autotools呢？<br><br>在开始体验功能强大的Autotools之前，简单（详细）回忆总结一下我使用make的经历和思考的过程，反省一下看看自己在接触这些新鲜事物的时候到底走了多少弯路。&nbsp;&nbsp;<a href='http://www.cppblog.com/Fox/archive/2009/12/23/autotools_first_use.html'>阅读全文</a><img src ="http://www.cppblog.com/Fox/aggbug/103743.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> 2009-12-23 02:18 <a href="http://www.cppblog.com/Fox/archive/2009/12/23/autotools_first_use.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE vs Boost: Singleton的实现</title><link>http://www.cppblog.com/Fox/archive/2009/09/22/96898.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 21 Sep 2009 16:38:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/09/22/96898.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/96898.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/09/22/96898.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/96898.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/96898.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090922/ace_boost_singleton.html/" target="_blank">游戏人生</a> <p>以前曾经讨论过<a href="http://www.yulefox.com/20081119/design-patterns-03.html/" target="_blank">Singleton的实现</a>，这次在对照ACE和Boost代码的时候，又重新审视了一下二者对Singleton不同的实现。其间的差别也体现了不同的编程哲学：ACE的实现更加偏重多线程中的安全和效率问题；Boost的实现则偏重于使用语言自身的特性满足Singleton模式的基本需求。  <p><strong>o ACE的实现</strong>  <p>Douglas C. Schmidt在<a href="http://www.cs.wustl.edu/~schmidt/PDF/DC-Locking.pdf" target="_blank">Double-Checked Locking: An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects</a>一文中对double-check lock（一般译为双检锁）进行了详细的阐述。  <p>ACE的Singleton使用Adapter模式实现对其他类的适配，使之具有全局唯一的实例。由于C++标准并非明确指定全局静态对象的初始化顺序，ACE使用double-check lock保证线程安全，并使之不受全局静态对象初始化顺序的影响，同时也避免了全局静态实现方式的初始化后不使用的开销。  <p>如果你能够准确的区分以下三种实现的弊端和隐患，对double-check lock也就有了足够的了解。  <blockquote><code> <p>// -------------------------------------------<br>class Singleton<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; static Singleton *instance (void)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Constructor of guard acquires<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // lock_ automatically.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Guard&lt;Mutex&gt; guard (lock_);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Only one thread in the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // critical section at a time.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (instance_ == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance_ = new Singleton;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return instance_;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Destructor of guard releases<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // lock_ automatically.<br>&nbsp;&nbsp;&nbsp; }<br>private:<br>&nbsp;&nbsp;&nbsp; static Mutex lock_;<br>&nbsp;&nbsp;&nbsp; static Singleton *instance_;<br>};  <p>// ---------------------------------------------<br>static Singleton *instance (void)<br>{<br>&nbsp;&nbsp;&nbsp; if (instance_ == 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Guard&lt;Mutex&gt; guard (lock_);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Only come here if instance_<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // hasn’t been initialized yet.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance_ = new Singleton;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return instance_;<br>}  <p>// ---------------------------------------------<br>class Singleton<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; static Singleton *instance (void)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // First check<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (instance_ == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Ensure serialization (guard<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // constructor acquires lock_).<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Guard&lt;Mutex&gt; guard (lock_);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Double check.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (instance_ == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance_ = new Singleton;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return instance_;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // guard destructor releases lock_.<br>&nbsp;&nbsp;&nbsp; }<br>private:<br>&nbsp;&nbsp;&nbsp; static Mutex lock_;<br>&nbsp;&nbsp;&nbsp; static Singleton *instance_;<br>};</p></code></blockquote> <p>更多详情，见Schmidt老师的原文和ACE_Singleton实现。  <p><strong>o Boost的实现</strong>  <p>Boost的Singleton也是线程安全的，而且没有使用锁机制。当然，Boost的Singleton有以下限制（遵从这些限制，可以提高效率）：  <p>o The classes below support usage of singletons, including use in program startup/shutdown code, AS LONG AS there is only one thread running before main() begins, and only one thread running after main() exits.  <p>o This class is also limited in that it can only provide singleton usage for classes with default constructors.  <blockquote><code> <p>// T must be: no-throw default constructible and no-throw destructible<br>template &lt;typename T&gt;<br>struct singleton_default<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; struct object_creator<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // This constructor does nothing more than ensure that instance()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; is called before main() begins, thus creating the static<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; T object before multithreading race issues can come up.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object_creator() { singleton_default&lt;T&gt;::instance(); }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inline void do_nothing() const { }<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; static object_creator create_object;  <p>&nbsp;&nbsp;&nbsp; singleton_default();  <p>public:<br>&nbsp;&nbsp;&nbsp; typedef T object_type;  <p>&nbsp;&nbsp;&nbsp; // If, at any point (in user code), singleton_default&lt;T&gt;::instance()<br>&nbsp;&nbsp;&nbsp; //&nbsp; is called, then the following function is instantiated.<br>&nbsp;&nbsp;&nbsp; static object_type &amp; instance()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // This is the object that we return a reference to.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // It is guaranteed to be created before main() begins because of<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; the next line.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static object_type obj;  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // The following line does nothing else than force the instantiation<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; of singleton_default&lt;T&gt;::create_object, whose constructor is<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; called before main() begins.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create_object.do_nothing();  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return obj;<br>&nbsp;&nbsp;&nbsp; }<br>};<br>template &lt;typename T&gt;<br>typename singleton_default&lt;T&gt;::object_creator<br>singleton_default&lt;T&gt;::create_object;</p></code></blockquote> <p>对于多数Singleton使用，Boost提供的版本完全能够满足需求。为了效率，我们有必要对其使用作出一定的限制。</p> <p>而在多线程编程中，则有必要使用double-check lock降低频繁加锁带来的开销。</p> <p>-------------------------------------------------------------------------------</p> <p>PS: 欣赏Soft的一句话：<strong>经得起诱惑，耐得住寂寞</strong>。</p><img src ="http://www.cppblog.com/Fox/aggbug/96898.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> 2009-09-22 00:38 <a href="http://www.cppblog.com/Fox/archive/2009/09/22/96898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IOCP使用时常见的几个错误</title><link>http://www.cppblog.com/Fox/archive/2009/09/12/95975.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Fri, 11 Sep 2009 16:20:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/09/12/95975.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/95975.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/09/12/95975.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/95975.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/95975.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090912/iocp_errors.html/" target="_blank">游戏人生</a></p> <p>在使用IOCP时，最重要的几个API就是GetQueueCompeltionStatus、WSARecv、WSASend，数据的I/O及其完成状态通过这几个接口获取并进行后续处理。</p> <p>GetQueueCompeltionStatus attempts to dequeue an I/O completion packet from the specified I/O completion port. If there is no completion packet queued, the function waits for a pending I/O operation associated with the completion port to complete.</p><pre><code>BOOL WINAPI GetQueuedCompletionStatus(
  __in   HANDLE <em>CompletionPort</em>,
  __out  LPDWORD <em>lpNumberOfBytes</em>,
  __out  PULONG_PTR <em>lpCompletionKey</em>,
  __out  LPOVERLAPPED *<em>lpOverlapped</em>,
  __in   DWORD <em>dwMilliseconds</em>
);</code></pre>
<p>If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the <em>lpNumberOfBytes</em>, <em>lpCompletionKey</em>, and <em>lpOverlapped</em> parameters.</p>
<p>除了关心这个API的in &amp; out（这是MSDN开头的几行就可以告诉我们的）之外，我们更加关心不同的return &amp; out意味着什么，因为由于各种已知或未知的原因，我们的程序并不总是有正确的return &amp; out。</p>
<p>If *<em>lpOverlapped</em> is NULL and the function does not dequeue a completion packet from the completion port, the return value is zero. The function does not store information in the variables pointed to by the <em>lpNumberOfBytes</em> and <em>lpCompletionKey</em> parameters. To get extended error information, call <a href="http://msdn.microsoft.com/en-us/library/ms679360(VS.85).aspx" target="_blank">GetLastError</a><strong></strong>. If the function did not dequeue a completion packet because the wait timed out, <strong>GetLastError</strong> returns WAIT_TIMEOUT.</p>
<p>假设我们指定<em>dwMilliseconds</em>为INFINITE。</p>
<p>这里常见的几个错误有：</p>
<p><strong>WSA_OPERATION_ABORTED (995): Overlapped operation aborted.</strong></p>
<p>由于线程退出或应用程序请求，已放弃I/O 操作。</p>
<p><strong>MSDN: </strong>An overlapped operation was canceled due to the closure of the socket, or the execution of the SIO_FLUSH command in <a href="http://msdn.microsoft.com/en-us/library/ms741621(VS.85).aspx" target="_blank">WSAIoctl</a><strong></strong>. Note that this error is returned by the operating system, so the error number may change in future releases of Windows.</p>
<p><strong>成因分析</strong>：这个错误一般是由于peer socket被<strong>closesocket</strong>或者<strong>WSACleanup</strong>关闭后，针对这些socket的pending overlapped I/O operation被中止。</p>
<p><strong>解决方案</strong>：针对socket，一般应该先调用shutdown禁止I/O操作后再调用closesocket关闭。</p>
<p><strong>严重程度</strong>：<strong>轻微易处理</strong>。</p>
<p><strong>WSAENOTSOCK (10038): Socket operation on nonsocket.</strong></p>
<p><strong>MSDN: </strong>An operation was attempted on something that is not a socket. Either the socket handle parameter did not reference a valid socket, or for <a href="http://msdn.microsoft.com/en-us/library/ms740141(VS.85).aspx" target="_blank">select</a><strong></strong>, a member of an <a></a><strong>fd_set</strong> was not valid.</p>
<p><strong>成因分析</strong>：在一个非套接字上尝试了一个操作。</p>
<p>使用<strong>closesocket</strong>关闭socket之后，针对该invalid socket的任何操作都会获得该错误。</p>
<p><strong>解决方案</strong>：如果是多线程存在对同一socket的操作，要保证对socket的I/O操作逻辑上的顺序，做好socket的graceful disconnect。</p>
<p><strong>严重程度</strong>：<strong>轻微易处理</strong>。</p>
<p><strong>WSAECONNRESET (10054): Connection reset by peer.</strong></p>
<p>远程主机强迫关闭了一个现有的连接。</p>
<p><strong>MSDN: </strong>An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see <a href="http://msdn.microsoft.com/en-us/library/ms740476(VS.85).aspx" target="_blank">setsockopt</a><strong></strong> for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.</p>
<p><strong>成因分析</strong>：在使用WSAAccpet、WSARecv、WSASend等接口时，如果peer application突然中止（原因如上所述），往其对应的socket上投递的operations将会失败。</p>
<p><strong>解决方案</strong>：如果是对方主机或程序意外中止，那就只有各安天命了。但如果这程序是你写的，而你只是hard close，那就由不得别人了。至少，你要知道这样的错误已经出现了，就不要再费劲的继续投递或等待了。</p>
<p><strong>严重程度</strong>：<strong>轻微易处理</strong>。</p>
<p><strong>WSAECONNREFUSED (10061): Connection refused.</strong></p>
<p>由于目标机器积极拒绝，无法连接。</p>
<p><strong>MSDN: </strong>No connection could be made because the target computer actively refused it. This usually results from trying to connect to a service that is inactive on the foreign host—that is, one with no server application running.</p>
<p><strong>成因分析</strong>：在使用connect或WSAConnect时，服务器没有运行或者服务器的监听队列已满；在使用WSAAccept时，客户端的连接请求被condition function拒绝。</p>
<p><strong>解决方案</strong>：Call <strong>connect</strong> or <strong>WSAConnect</strong> again for the same socket. 等待服务器开启、监听空闲或查看被拒绝的原因。<strong>是不是长的丑或者钱没给够，要不就是服务器拒绝接受天价薪酬自主创业去了？</strong></p>
<p><strong>严重程度</strong>：<strong>轻微易处理</strong>。</p>
<p><strong>WSAENOBUFS (10055): No buffer space available.</strong></p>
<p>由于系统缓冲区空间不足或列队已满，不能执行套接字上的操作。</p>
<p><strong>MSDN: </strong>An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.</p>
<p><strong>成因分析</strong>：这个错误是我查看错误日志后，最在意的一个错误。因为服务器对于消息收发有明确限制，如果缓冲区不足应该早就处理了，不可能待到send/recv失败啊。而且这个错误在之前的版本中几乎没有出现过。这也是这篇文章的主要内容。像connect和accept因为缓冲区空间不足都可以理解，而且危险不高，但如果send/recv造成拥堵并恶性循环下去，麻烦就大了，至少说明之前的验证逻辑有疏漏。</p>
<p>WSASend失败的原因是：The Windows Sockets provider reports a buffer deadlock. 这里提到的是buffer deadlock，显然是由于多线程I/O投递不当引起的。</p>
<p><strong>解决方案</strong>：在消息收发前，对最大挂起的消息总的数量和容量进行检验和控制。</p>
<p><strong>严重程度</strong>：<strong>严重</strong>。</p>
<p>本文主要参考<a href="http://msdn.microsoft.com/" target="_blank">MSDN</a>。</p>
<p><strong>************* 说明 *************</strong></p>
<p>Fox只是对自己关心的几个错误和API参照MSDN进行分析，不提供额外帮助。</p><img src ="http://www.cppblog.com/Fox/aggbug/95975.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> 2009-09-12 00:20 <a href="http://www.cppblog.com/Fox/archive/2009/09/12/95975.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Amdahl 定律 = Gustafson定律</title><link>http://www.cppblog.com/Fox/archive/2009/09/10/95782.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Thu, 10 Sep 2009 04:04:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/09/10/95782.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/95782.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/09/10/95782.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/95782.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/95782.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090910/amdahl_vs_gustafson.html/" target="_blank">游戏人生</a></p> <p>周伟明老师应该是多核计算领域的老人了。</p> <p>这几日因为想找找无锁（lock-free）方面的信息，就打开了<a href="http://blog.csdn.net/drzhouweiming/" target="_blank">周老师的blog</a>。看到<a href="http://blog.csdn.net/drzhouweiming/archive/2007/09/25/1800319.aspx" target="_blank">多核系统中三种典型锁竞争的加速比分析</a>这篇文章时，觉得老师强调多核计算效率是有必要的，但拿Amdahl 定律和Gustafson定律作对比有点不恰当。</p> <p>按照我的理解，这两个定律所刻画的内容是完全一致的，只是对加速比的定义不一样罢了。这里，我们都以S(n)表示n核系统对具体程序的加速比，K表示串行部分计算时间比例。</p> <p>Amdahl 定律的加速比：S(n) ＝ 使用1个处理器的串行计算时间 / 使用n个处理器的并行计算时间</p> <p>S(n) = 1/(K+(1-K)/n) = n/(1+(n-1)K)</p> <p>Gustafson定律的加速比：S(n) ＝ 使用n个处理器的并行计算量 / 使用1个处理器的串行计算量</p> <p>S(n) = K+(1-K)n</p> <p>通俗的讲，Amdahl 定律将工作量看作1，有n核也只能分担1-K的工作量；而Gustafson定律则将单核工作量看作1，有n核，就可以增加n(1-K)的工作量。</p> <p>这两个计算公式都没有将锁开销考虑在内，是理想化的。周老师提到设计不当造成并行变串行的问题与这两个公式计算无关。因为任何多核计算都存在对串行和并行的设计考量，这正是程序员在使用多核并行时最关心的事情。</p> <p>总之，二者的区别只在于态度的不同：一个消极悲观，一个积极乐观，充其量是一个冷笑话，而于多核计算没有任何关联。</p> <p>我说这些也与多核计算没有关联，丝毫没有质疑多核效率的意思。相反，我期待能够通过技术层面提高多核的有效负载。</p>最后一句题外话，周老师使用Word的水平一般：所有来自Word的截图都是在页面视图直接截，换行符和光标随处可见。<img src ="http://www.cppblog.com/Fox/aggbug/95782.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> 2009-09-10 12:04 <a href="http://www.cppblog.com/Fox/archive/2009/09/10/95782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE: Socket封装（01）</title><link>http://www.cppblog.com/Fox/archive/2009/09/01/94973.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 01 Sep 2009 06:22:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/09/01/94973.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/94973.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/09/01/94973.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/94973.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/94973.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090901/ace_socket_wrapper_facades.html/" target="_blank">游戏人生</a></p> <p><strong>o *__ 序 __* o</strong></p> <p>在阅读ACE代码和C++NPv1, v2, APG的时候，我意识到一个问题：虽然稍有C++和网络基础的同学都可以读懂ACE，但如果你对OS（五大管理模块都包含在内）、TCP/IP、C++、Design Patterns了解越多，你就越能体会ACE为什么需要这么庞杂，虽然它不够完美（但至少我还没有资格来批评这一点，我现在最常想做的一个动作就是五体投地）。</p> <p>而且我隐约感觉到，我现在所写的很多东西在以后（对于有些人或许就是现在）看来会相当不深刻、相当不严谨，但对于一段学习历程，这个过程是必然的、必需的。</p> <p>在C++NPv1中，<a href="http://www.informit.com/safari/author_bio.asp@ISBN=0201604647" target="_blank">Douglas C. Schmidt</a>把原始socket及其API的缺陷有些妖魔化了，比如一段加上注释、空行在内的35行的代码，被指出有10处错误之多。这就像很多其他语言的倡导者或反传统C/C++指针者在批评指针时的说法一样。长期使用原始socket和指针的同学对此感觉很不舒服，何况socket API提供了大量错误检测的接口，至多是不够友好罢了。你好就好了，没必要抓住别人一顿痛批吧，『本是同根生，相煎何太急』。</p> <p>虽然Solaris、Linux的很多版本及Windows对起源于Berkeley的socket API进行了重写，但不可否认，由于历史原因和POSIX标准的存在，对于使用者而言，我们可以无视这些API的实现差异。只是一旦我们从socket通信扩展到其他IPC通信的话，就需要正视各种I/O细节的差异了。</p> <p>由于UNIX中，对于socket, file, pipe, device的大多数操作，描述符都是通用的（这一点，OS上面讲的更清楚些）。而Windows中，句柄大多不能互换（socket对于MS来说是舶来品）。系统和标准的不一致导致地址、协议和API的混杂甚至混乱。</p> <p>UNIX下的描述符和Windows的句柄可以看作是同一个概念，只是应用环境不一样，所描述的内容也时常不一样，再简单了说，它们都是一个整型的ID。</p> <p>ACE的源码中使用了大量预处理指令，尤其在跨平台/编译环境的部分更加明显。鉴于C/C++标准的博大胸怀，有些指令需要阅读相关编译器提供的帮助文档：</p> <p>o #pragma: <a href="http://gcc.gnu.org/onlinedocs/cpp/Pragmas.html#Pragmas" target="_blank">GCC</a>, <a href="http://msdn.microsoft.com/en-us/library/d9x1s805(VS.71).aspx" target="_blank">MSVC</a></p> <p>o #define (#, #@, ##) : <a href="http://gcc.gnu.org/onlinedocs/cpp/Macros.html#Macros" target="_blank">GCC</a>, <a href="http://msdn.microsoft.com/en-us/library/wy090hkc(VS.80).aspx" target="_blank">MSVC</a> </p> <p>其中有若干代码文件以.inl为后缀，里面是对部分函数的内联实现，以使代码结构看上去更加简洁。如果确定使用内联函数的话，*.inl将被包含于*.h的最后，如果不使用，则像*.h一样，包含于*.cpp的头部。</p> <p>ACE采用doxygen输出文档，在阅读代码注释时能够感受到差异，但基本不会影响阅读。</p> <p><strong>o * __ 关于第3章（C++NPv1）__ * o</strong></p> <p>ACE抽象的地址类ACE_Addr拥有ACE_DEV_Addr, ACE_FILE_Addr, ACE_INET_Addr, ACE_SPIPE_Addr, ACE_UNIX_Addr五个子类。对于狭义上的网络通信（TCP/IP）而言，ACE_INET_Addr对应于我们熟悉的sockaddr_in。</p> <p>ACE_IPC_SAP是IPC（interprocess communication）I/O操作类的root类。</p> <p>从编码的角度看，这个类漂亮的地方在于示例了抽象类的另一种实现方式。</p> <p>一提到抽象类，大多数人的第一反应是pure virtual function。当一个基类确定需要使用virtual function时，这是一个不错的选择。但我们都知道虚拟函数有开销。而且对于一个结构简单的抽象基类和其继承子类（尤其是大量使用时），一个虚函数表带来的开销会让整个设计显得十分蹩脚。</p> <p>我们都知道如何强制让一个类无法使用default constructor（protected）。如果对基类使用该方法，仅使子类具有public的default constructor，这就达到了定义抽象基类的效果。</p> <p>virtual destructor的意义在于防止delete父类指针（指向子类对象）时未调用子类destructor。在此例中，为避免这种情况，同样将destructor声明为protected即可。</p> <p>从设计实现的角度看，相较于socket API，ACE_IPC_SAP的子类ACE_SOCK提供了编译时对句柄合法性的检测。</p> <p>从逻辑功能层面划分，socket有三种角色：</p> <p>o active connection role (connector)：主动连接</p> <p>o passive connection role (acceptor)：被动连接</p> <p>o communication role (stream)：数据通信</p> <p>但socket API毕竟不是OOD出来的，对于一个socket描述符，也完全没有必要去限制其担负的功能，更不可能搞成三种不同的socket。而OOD的ACE则可以轻易实现对socket对象及其操作的封装。</p> <p>工厂类ACE_SOCK_Connector是一个主动创建通信端的工厂类。socket API中的connect接口只是为一个socket建立与其它peer的网络连接，而不产生新的socket实例，也不依赖于任何其它socket。同样，ACE_SOCK_Connector只是为一个ACE_SOCK_Stream对象（对用于数据通信的socket的封装）连接到ACE_Addr（对struct sockaddr的封装）提供接口，也不含对ACE_SOCK_Stream对象的其它操作。</p> <p>工厂类ACE_SOCK_Acceptor是一个被动创建通信端的工厂类。当监听到新的网络连接后，为该连接初始化一个ACE_SOCK_Stream对象。和connector不同的是，acceptor依赖于一个已经存在的充当监听功能的socket句柄（ACE_SOCK），因此，ACE_SOCK_Acceptor是ACE_SOCK的一个子类。</p> <p>ACE_SOCK_Stream是只负有通信传输功能的socket，对应connection-oriented的TCP通信格式stream，和UDP的CE_SOCK_CODgram相呼应。ACE_SOCK_Stream只是socket的通信载体，在两个工厂ACE_SOCK_Connector和ACE_SOCK_Acceptor中初始化。这样一个类除支持最基本的数据发送（send）和接收（recv）和阻塞（blocking）、非阻塞（nonblocking）及定时（timed）的I/O模式外，还支持分散读取（scatter-read）和集中写入（gather-write）。</p> <p>对于一个简单的『网络课程作业：写一个有连接的IM小程序』，上面这些内容已经足够了。当然即使使用对应的几个socket API也已经足够了。但我们显然更加关心如此庞大的一个库，是如何解决复杂的网络应用的，我尤其关心的是多线程并发如何更好的处理。</p> <p>所以，我准备跑到第8、9章了。</p><img src ="http://www.cppblog.com/Fox/aggbug/94973.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> 2009-09-01 14:22 <a href="http://www.cppblog.com/Fox/archive/2009/09/01/94973.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Doxygen在Cygwin下的使用</title><link>http://www.cppblog.com/Fox/archive/2009/08/28/94689.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Fri, 28 Aug 2009 09:14:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/08/28/94689.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/94689.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/08/28/94689.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/94689.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/94689.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090828/usage_doxygen_cygwin.html/" target="_blank">游戏人生</a></p> <p>我发现我最近成了Cygwin下的小白鼠，写完<a href="http://www.yulefox.com/20090817/install_ace_cygwin.html/" target="_blank">Cygwin下安装ACE</a>，写<a href="http://www.yulefox.com/20090819/usage_ace_cygwin.html/" target="_blank">ACE在cygwin下的使用</a>。现在又写<a href="http://www.stack.nl/~dimitri/doxygen/" target="_blank">doxygen</a>。</p> <p>之前提到在Cygwin下读代码的不习惯，后来回到VS下看。没过几天，觉得VS下还是不够直观，于是就直接看<a href="http://www.dre.vanderbilt.edu/Doxygen/" target="_blank">ACE的doxygen</a>了&#8230;&#8230;</p> <p>doxygen好是好，用起来还是要慢慢习惯才行，需要在写注释和代码的时候注意一些，掌握的细节和技巧越多，出来的文档越丰富（当然，这和代码质量是两码事）。</p> <p>我自然是把doxygen安装在Cygwin下了，由于doxygen没有提供info，Info doxygen时就自动打开了doxygen的man，和man doxygen、doxygen --help一个效果。</p> <p>如果希望阅读更详尽的使用方法，只有自己<a href="http://www.stack.nl/~dimitri/doxygen/download.html" target="_blank">down一个manual了</a>。</p> <p>在Cygwin下，doxygen采用<a href="http://www.gnu.org/software/libiconv/" target="_blank">GNU的libiconv</a>进行文字编码的转换，以<a href="http://zh.wikipedia.org/wiki/UTF-8" target="_blank">UTF-8</a>作为默认编码。</p> <p>使用doxygen生成config-file模板后，可以在config-file中进行一些项目设置（有注释的，看的懂）。</p> <p>为了支持中文，我DOXYFILE_ENCODING用的是<a href="http://zh.wikipedia.org/wiki/EUC" target="_blank">EUC-CN</a>，但输出文档的语言OUTPUT_LANGUAGE却选了English。两点原因：</p> <p>o EUC-CN（各种汉字编码知识就不在此普及了，你可以认为简体字编码都是EUC-CN）和UTF-8不同，但OUTPUT_LANGUAGE的各种语言都是使用的UTF-8，<span  style="text-decoration: line-through;">所以两种编码不可能同时显示，当然，你可以把EUC-CN全转成UTF-8。编码不是高级的技术，但对于非英语用户绝对是一个噩梦</span>，<span  style="color: rgb(34, 34, 34); font-family: Georgia, Arial; font-size: 14px; ">后来发现是我自己学艺不精，DOXYFILE_ENCODING只是配置文件的编码格式而已，而识别中文文档只需要修改INPUT_ENCODING成EUC-CN即可，OUTPUT_LANGUAGE自然设置成Chinese也不会有问题，因为doxygen采用UTF-8输出，使用中文输出不会有乱码问题<span  style="color: rgb(0, 0, 0); font-family: 'Times New Roman'; font-size: medium; ">。</span></span></p> <p>o 虽然我的英文很蹩脚，虽然我的文档中多有中文注释。但像doxygen中文输出的文档中把class、public都给你翻译成中文，你也受不了，这也英文水平无关。</p> <p>config-file中的其他内容我现在也用不到，就没有仔细看。</p> <p>因为<a href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin</a>会在公司里讲一下doxygen，doxygen的manual也讲的很详细，我就省点时间，不翻译文档了。</p> <p>简单的一个<a href="http://www.yulefox.com/doc/test/html/index.html" target="_blank">Doxygen的测试</a>在这里。</p><img src ="http://www.cppblog.com/Fox/aggbug/94689.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> 2009-08-28 17:14 <a href="http://www.cppblog.com/Fox/archive/2009/08/28/94689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE之Cygwin vs Win</title><link>http://www.cppblog.com/Fox/archive/2009/08/24/94234.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 24 Aug 2009 03:59:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/08/24/94234.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/94234.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/08/24/94234.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/94234.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/94234.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090824/ace_cygwin_win.html/" target="_blank">游戏人生</a></p> <p>我屈服了，还是VS用的方便。</p> <p><a href="http://www.yulefox.com/20090819/usage_ace_cygwin.html/" target="_blank">之前在Cygwin下已经可以使用的ACE</a>，因为阅读代码太不方便（对于一个WinEr来说），上午在VS下面花了几分钟就把ACE配好了，而且使用$(ACE_ROOT)\examples\C++NPv1的代码跟踪调试，太习惯了。</p> <p>按照$(ACE_ROOT)\ACE-INSTALL.html的安装说明：</p> <p>o 选择并打开$(ACE_ROOT)\ace\ace_vc9.sln</p> <p>o 添加config.h并加入以下内容：</p> <p><font face="新宋体">&nbsp;<font face="新宋体">&nbsp;&nbsp; #define ACE_HAS_STANDARD_CPP_LIBRARY 1</font> <br>&nbsp;&nbsp;&nbsp; #include "ace/config-win32.h"</font></p> <p>o F7 </p> <p>-----------------------------------------------  <p>OK，现在$(ACE_ROOT)\lib下面已经生成了ACEd.dll、ACEd.lib，再设置一下系统环境变量（运行程序必需）和VC++目录（调试程序必需）。可以使用了：  <p>o 选择并打开$(ACE_ROOT)\examples\C++NPv1  <p>o F7</p> <p>o <font face="新宋体">for (; ; ) { F12, F9, F5, F10, F11 }</font></p> <p>-----------------------------------------------</p> <p>半个小时就搞定了当时一个星期的折腾……</p> <p><strong>结论：对于一个不忠实的Win Coder，在MinGW, Cygwin, UNIX…下面装B是要付出代价的。</strong></p>当然，家里的机器就让它还一直跑Cygwin吧。<img src ="http://www.cppblog.com/Fox/aggbug/94234.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> 2009-08-24 11:59 <a href="http://www.cppblog.com/Fox/archive/2009/08/24/94234.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE在cygwin下的使用</title><link>http://www.cppblog.com/Fox/archive/2009/08/19/93781.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Wed, 19 Aug 2009 02:05:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/08/19/93781.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/93781.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/08/19/93781.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/93781.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/93781.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090819/usage_ace_cygwin.html/" target="_blank">游戏人生</a>  <p><font face="新宋体">/*--------- Hello.cc ---------*/  <p>/** Hello.cc:<br>* @File:&nbsp;&nbsp; Hello.cc<br>* @Author: Fox &lt;yulefox at gmail dot com&gt;<br>* @Date:&nbsp;&nbsp; Aug. 19th, 2009<br>* @Brief:&nbsp; Test ACE log module application<br>*/</p> <p>#define ACE_NTRACE 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// trace the calling position </p> <p>#include "ace/Log_Msg.h"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// include log module  <p>int ACE_TMAIN(int, ACE_TCHAR *[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; ACE_TRACE(ACE_TEXT("main"));  <p>&nbsp;&nbsp;&nbsp;&nbsp; ACE_DEBUG((LM_INFO, ACE_TEXT("%IStart\n")));<br>&nbsp;&nbsp;&nbsp;&nbsp; ACE_DEBUG((LM_INFO, ACE_TEXT("%IEnd\n")));  <p>&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}  <p>/*--------- makefile ---------*/  <p>BIN&nbsp;&nbsp;&nbsp;&nbsp; = hello&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # src &amp; exe file name<br>SRC&nbsp;&nbsp;&nbsp;&nbsp; = $(addsuffix .cc, $(BIN))&nbsp;&nbsp;&nbsp; # src file suffix<br>LIBS&nbsp;&nbsp;&nbsp; = -lACE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # libACE.dll under cygwin  <p>include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU<br>include $(ACE_ROOT)/include/makeinclude/macros.GNU<br>include $(ACE_ROOT)/include/makeinclude/rules.common.GNU<br>include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU<br>include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU<br>include $(ACE_ROOT)/include/makeinclude/rules.local.GNU  <p>/*--------- Compilation ---------*/  <p>GNUmakefile: /home/fox/ace/GNUmakefile MAKEFLAGS=k  <p>g++ -Wpointer-arith -mthreads -mtune=pentiumpro -O3 -g -pipe&nbsp;&nbsp;&nbsp; -pipe&nbsp;&nbsp; -I/usr/\<br>share/ace -DACE_HAS_EXCEPTIONS -DACE_NO_INLINE&nbsp; -c -o .obj/hello.o hello.cc<br>g++ -Wpointer-arith -mthreads -mtune=pentiumpro -O3 -g -pipe&nbsp;&nbsp;&nbsp; -pipe&nbsp;&nbsp; -I/usr/\<br>share/ace -DACE_HAS_EXCEPTIONS -DACE_NO_INLINE&nbsp; -Wl,--enable-auto-import -Wl,-E\<br>-L/usr/share/ace/lib -o hello .obj/hello.o&nbsp; -lACE  <p>Compilation finished at Wed Aug 19 00:35:42  <p>/*--------- Result ---------*/  <p>$ ./hello.exe<br>(14417928) calling main in file `hello.cc' on line 13<br>&nbsp;&nbsp;&nbsp; Start<br>&nbsp;&nbsp;&nbsp; End<br>(14417928) leaving main</font></p> <p>-------------------------------------------------------</p> <p>更多内容请参考<a href="http://www.cse.wustl.edu/~schmidt/ACE/" target="_blank">C++NP(C++ Network Programming) vol.1 &amp; vol.2和APG(The ACE Progrmmer's Guide)</a>。</p> <p>忙活了一晚上，终于知道怎么包含头文件了，在gcc的编译选项中用 -I或/I$(ACE_ROOT)：</p> <p>本例中是：-I/usr/share/ace</p> <p>结果后面库又链接不上，联想以前使用OpenGL库的LIBS，终于靠一个-lACE搞定。</p> <p>因为不愿意用MPC，总感觉再多花些时间去弄又只是离题更远了，有兴趣的同学自然是可以通过ACE的官网找到所有问题的答案。</p> <p>这样一来，<a href="http://www.yulefox.com/20090817/install_ace_cygwin.html/" target="_blank">ACE在cygwin下从安装</a>到使用也就告一段落了，后面的问题就比较easy了，无非是你用ACE做什么。而我也不会再就ACE &amp;&amp; cygwin写什么心得了，总算见证了这两天的捣腾。</p> <p><img style="display: block; float: none; margin-left: auto; margin-right: auto" src="http://www.yulefox.com/img/090819_ace_cygwin.jpg" width="640" height="321"></p><img src ="http://www.cppblog.com/Fox/aggbug/93781.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> 2009-08-19 10:05 <a href="http://www.cppblog.com/Fox/archive/2009/08/19/93781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>与君共勉</title><link>http://www.cppblog.com/Fox/archive/2009/08/17/93620.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 17 Aug 2009 08:31:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/08/17/93620.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/93620.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/08/17/93620.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/93620.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/93620.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com/20090817/excite_with_you.html/" target="_blank">游戏人生</a></p> <p>现在有些年轻人的心态比较浮躁，大致总结以下『四点表现』：</p> <p>1. 眼高过顶；</p> <p>2. 垂手过膝；</p> <p>3. 期望值高；</p> <p>4. 积极性低。</p> <p>深层次的客观原因大致是『四个没有』：</p> <p>1. 没有吃过苦；</p> <p>2. 没有干过活；</p> <p>3. 没有说过话；</p> <p>4. 没有当过家。</p> <p>一般都具有『四个特征』：</p> <p>1. 独生子女；</p> <p>2. 毕业新人；</p> <p>3. 沉默寡言；</p> <p>4. 半瓶开水。</p> <p>---------------------------------</p> <p>1. 你想要什么？</p> <p>参考：一个有影响力的人。（<a href="https://twitter.com/kaifulee" target="_blank">@李开复</a>）</p> <p>2. 那是否是你想要的？</p> <p>参考：你的眼光有多远，决定了你能走多远。（<a href="https://twitter.com/yulefox" target="_blank">@Fox</a>）</p> <p>3. 你需要做什么？</p> <p>参考：高筑墙，广积粮，缓称王。（朱元璋）</p> <p>4. 你还需要做什么？</p> <p>参考：平和的心态，进取的态度，坚定的目标，不懈的努力。（<a href="https://twitter.com/yulefox" target="_blank">@Fox</a>）</p> <p>5. 你是否做到了？</p> <p>参考：没有，但我一直在努力。（<a href="https://twitter.com/yulefox" target="_blank">@Fox</a>）</p><img src ="http://www.cppblog.com/Fox/aggbug/93620.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> 2009-08-17 16:31 <a href="http://www.cppblog.com/Fox/archive/2009/08/17/93620.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cygwin下安装ACE</title><link>http://www.cppblog.com/Fox/archive/2009/08/17/93552.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Mon, 17 Aug 2009 01:52:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/08/17/93552.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/93552.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/08/17/93552.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/93552.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/93552.html</trackback:ping><description><![CDATA[<p>本文同步自<a href="http://www.yulefox.com">游戏人生</a>  <p>-----------------------读书-----------------------  <p>想读<a href="http://www.douban.com/subject/1500149/" target="_blank">《UNIX Network Programming》</a>很久了，只是这种愿望一直没有特别强烈。用的笔记本换了之后强迫自己只是装了cygwin，没有安装VS，平时写些代码就只能在cygwin下用gcc了。最近对UNIX环境编程和网络基础比较感兴趣，于是读这本书终于提上了议事日程，可是大多数书店都没有这本书了。  <p>工作之后，对于自己喜欢的书，就很少会去看电子版，觉得还是捧卷在手的感觉舒服一些，想怎么看怎么看，享受拿笔在上面写写画画的感觉。因此对于想读但买不到的书，第一反应是等待。在翻电脑上面一些资料的时候才反应过来我已经很久没有down过电子书了，结果就从网上down了<a href="http://www.douban.com/book/list/yulefox/wish" target="_blank">很多想读(豆瓣)的书</a>，forgive me, Richard Stevens and anybody.  <p>-----------------------动手-----------------------  <p>之前只是在Windows下用过ACE，因为ACE提供了VS各种版本的解决方案，编译过程比较简单。这次放在cygwin下编译的时候，因为环境变量设置问题，并不是非常顺利，参照了源码目录下的ACE-INSTALL.html的描述才将问题解决。现在将过程给出来，仅供google到这里的同学参考：  <p>友情提示：我目前对于cygwin和UNIX类系统的了解停留在知其然（还是在google之后）的水平。  <p>1. 增加环境变量  <p>o 修改/etc/profile文件读写属性：  <p>$ chmod 777 /etc/profile  <p>cygwin下的环境变量可以在该文件中配置，由于该文件默认具有写保护属性，因此需要修改使之可写，修改完成之后应该记得对其进行写保护：  <p>$ chmod 555 /etc/profile  <p>o 打开profile文件（本人使用emacs进行编辑，读者也可以使用vi、vim等，在此不再赘述）：  <p>$ emacs /etc/profile  <p>o 往profile中修改和添加环境变量：  <p>将以下五行添加到文件中，ACE_ROOT为ACE源码目录，本文用到的目录是/usr/share/ace：  <p>ACE_ROOT=/usr/share/ace<br>export ACE_ROOT  <p>LD_LIBRARY_PATH=$ACE_ROOT/ace:$LD_LIBRARY_PATH<br>export LD_LIBRARY_PATH  <p># export PATH=$ACE_ROOT/ace:$PATH  <p>这个操作等同于在VS下修改库文件目录，我是直接修改了PATH变量的：  <p>PATH=/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:$ACE_ROOT/ace:$PATH  <p>o 执行/etc/profile  <p>$ /etc/profile  <p>执行修改后有可能需要重启cygwin，可使用下面的命令查看ACE_ROOT设置是否正确：  <p>$ echo $ACE_ROOT  <p>2. 创建若干文件：  <p>o 在$ACE_ROOT/ace 文件夹中创建名为 config.h 的文件，并加入以下内容后保存关闭：  <p>#include "ace/config-cygwin32.h"  <p>o 在$ACE_ROOT/include/makeinclude 文件夹中创建名为 platform_macros.GNU 的文件，并加入以下内容保存关闭：  <p>include $(ACE_ROOT)/include/makeinclude/platform_cygwin32.GNU  <p>3. 编译ACE：  <p>$ cd $ACE_ROOT/ace<br>$ make  <p>大概会花掉十几分钟时间吧。  <p>4. 测试ACE：  <p>$ cd $ACE_ROOT/tests<br>$ make  <p>大概又会花掉十几分钟时间吧。  <p>接下来就可以使用perl脚本完成所有测试，我没有使用过perl，临时安装了一下。  <p>$ perl run_test.pl  <p>这个脚本在我一位同事的机器上跑了很久（十几分钟 or 几十分钟？），不幸的是，Windows竟然抛出了一个内存读写错误，更加不幸的是，我现在没有能力去确定。  <p>结果周末后面的时间又花在熟悉EMacs上了，还没有看ACE的代码，只有下周才能看了。  <p>-----------------------结束-----------------------</p> <p>这种小学生的东西拿出来讲确实是很难为情的，然而，于我现在却又算是极大的一个收获。真等我熟悉了其中的很多内容，又未必会再有耐心说了。所以还是记录一下，既是鼓励，也是督促。</p> <p>晚上睡觉之前翻了一下<a href="http://www.douban.com/subject/1192062/" target="_blank">《ACE程序员指南：网络与系统编程的实用设计模式》</a>，发现里面2.3节也讲到了怎么安装。</p><img src ="http://www.cppblog.com/Fox/aggbug/93552.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> 2009-08-17 09:52 <a href="http://www.cppblog.com/Fox/archive/2009/08/17/93552.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式（四）&amp;mdash;&amp;mdash;Template Method</title><link>http://www.cppblog.com/Fox/archive/2009/02/11/73420.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Tue, 10 Feb 2009 17:46:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/02/11/73420.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/73420.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/02/11/73420.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/73420.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/73420.html</trackback:ping><description><![CDATA[<p>在一个稍微上规模（怎么也有几十个类吧）的C++程序中，继承和组合的使用比比皆是。虽然GoF提出了OOD的两个原则，第二个谓之『<strong>优先使用对象组合，而不是继承</strong>』，但这绝不意味着不使用继承，正因如此，第一个原则才是『<strong>针对接口编程，而不是针对实现编程</strong>』，这两个原则清楚的表达了软件工程中『<strong>低耦合</strong>』的思想。</p> <p>模板方法（Template Method）的意图正是『<strong>定义一个操作中的算法的框架，而将一些步骤延迟到子类中</strong>』。</p> <p>注意，这里有个关键词『一些』，既然是『一些』，就意味着父类的的接口中已经实现了『一些』步骤，否则，父类便只提供了抽象接口。</p> <p>这一情况是我们在编码时时常遇到的，父类实现了部分算法中的通用行为，子类根据自己的需求可对其进行必要扩充。</p> <p><code>class CBase<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; // Default done<br>&nbsp;&nbsp;&nbsp; virtual void Operation(void)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; }<br>}; </p> <p>class CDerive :<br>&nbsp;&nbsp;&nbsp; public CBase<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual void Operation(void)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch( ... )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case A: ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case B: ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case c: ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CBase::Operation();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>};</code></p> <p>因为对父类的不了解或是对子类的太了解，你以为<code>CDerive::Operation()</code>做了足够多，不需要<code>CBase::Operation()</code>，但实际上仍然需要的情况总在发生。</p> <p>换一种方式，把<code>Operation()</code>作为模板方法，把<code>DoOperation()</code>作为可扩展的接口提供给子类实现：</p> <p><code>class CBase<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; void SetFocus(void) { ... }<br>&nbsp;&nbsp;&nbsp; void ResetFocus(void) { ... }<br>&nbsp;&nbsp;&nbsp; // Call by user<br>&nbsp;&nbsp;&nbsp; void Operation(void)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFocus();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoOperation();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResetFocus();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; // Default done<br>&nbsp;&nbsp;&nbsp; virtual void DoOperation(void) = 0;<br>};  <p>class CDerive :<br>&nbsp;&nbsp;&nbsp; public CBase<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; virtual void DoOperation(void)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; }<br>};</code></p> <p>模板方法只是提供了一个简单的封装技巧，当然不是所有的虚接口都这么写:)。</p> <hr>  <p>更多内容请移步<a href="http://www.yulefox.com" target="_blank">我的个人主页</a>。</p><img src ="http://www.cppblog.com/Fox/aggbug/73420.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> 2009-02-11 01:46 <a href="http://www.cppblog.com/Fox/archive/2009/02/11/73420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VS2005断点失效的问题</title><link>http://www.cppblog.com/Fox/archive/2009/01/04/71110.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Sun, 04 Jan 2009 03:04:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2009/01/04/71110.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/71110.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2009/01/04/71110.html#Feedback</comments><slash:comments>19</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/71110.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/71110.html</trackback:ping><description><![CDATA[<p>VS2005下使用VC，部分断点无效，显示『当前不会命中断点。还没有为该文档加载任何符号』。</p> <p>试过以下一些方法：</p> <p>1、无效断点所在的项目和启动项目的设置：项目-&gt;属性-&gt;配置属性-&gt;C/C++-&gt;常规-&gt;调试信息格式，这里不能为『禁用』；</p> <p>2、项目-&gt;属性-&gt;配置属性-&gt;链接器-&gt;调试-&gt;生成调试信息，这里设为『是』；</p> <p>3、C/C++-&gt;优化-&gt;优化选择『禁用』；</p> <p>4、删除解决方案下的.ncb文件；</p> <p>5、工具-&gt;选项-&gt;调试-&gt;『要求源文件与原始版本完成匹配』去掉勾；</p> <p>6、最后在上述设置的情况下，重新编译整个解决方案；</p> <p>7、回过头来，发现原来是一段不会被执行到的代码……</p>看来，除了VS本身会有bug，自己的代码还是要多检查一下。<img src ="http://www.cppblog.com/Fox/aggbug/71110.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> 2009-01-04 11:04 <a href="http://www.cppblog.com/Fox/archive/2009/01/04/71110.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简单的属性结构设计</title><link>http://www.cppblog.com/Fox/archive/2008/12/28/70542.html</link><dc:creator>Fox</dc:creator><author>Fox</author><pubDate>Sat, 27 Dec 2008 18:44:00 GMT</pubDate><guid>http://www.cppblog.com/Fox/archive/2008/12/28/70542.html</guid><wfw:comment>http://www.cppblog.com/Fox/comments/70542.html</wfw:comment><comments>http://www.cppblog.com/Fox/archive/2008/12/28/70542.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Fox/comments/commentRss/70542.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Fox/services/trackbacks/70542.html</trackback:ping><description><![CDATA[<p>本文最早发布于<a href="http://www.yulefox.com/index.php/20081228/simple-attribute-design.html/" target="_blank">我的个人主页</a></p> <p>一般的RPG游戏中，玩家角色、NPC、物品、场景等一般都具有为数众多的各种属性，使用C++编码时，很容易考虑设计成为大量的成员变量和相应的存取函数：</p><code> <p>class CObject<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; long GetAttrA(void) const { return m_lAttrA; }<br>&nbsp;&nbsp;&nbsp; void SetAttrA(long lVal) { m_lAttrA = lVal; }<br>&nbsp;&nbsp;&nbsp; long GetAttrB(void) const { return m_lAttrB; }<br>&nbsp;&nbsp;&nbsp; void SetAttrB(long lVal) { m_lAttrB = lVal; }<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; long GetAttrN(void) const { return m_lAttrN; }<br>&nbsp;&nbsp;&nbsp; void SetAttrN(long lVal) { m_lAttrN = lVal; }  <p>private:<br>&nbsp;&nbsp;&nbsp; long m_lAttrA;<br>&nbsp;&nbsp;&nbsp; long m_lAttrB;<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; long m_lAttrN;<br>};</p></code> <p>乍一看，非常清晰明了。在一个稍显复杂的项目中，想记住、甚至找到每一个属性是非常难的，况且除了属性，还有大量的逻辑处理，甚至是不同项目间的数据交互，比如将属性的数据库存取。这么做的缺点大致有这么几点：</p> <p>1. 属性没有明确分门别类，尤其在多人协作、多模块编写时，往往上面一批、下面一批，甚至有重复属性、废弃属性，难于管理和把握；</p> <p>2. 对于数据库存取，需要写单独的存取接口，而且一旦属性有增减、修改，存取接口要随之修改；</p> <p>3. 通过接口函数进行简单的属性存取面临大量的堆栈保存，即使使用内联或宏定义，也是治标不治本。</p> <p>针对上述问题，我的思路也比较简单：对<strong>基本类型</strong>数据进行二次封装。</p><code> <p>struct tagDBAttrs<br>{<br>&nbsp;&nbsp;&nbsp; long lA;<br>&nbsp;&nbsp;&nbsp; long lB;<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; long lN;<br>};  <p>class CObject<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; const tagDBAttrs&amp; GetDBAttrs(void) const { return m_DBAttrs; }<br>&nbsp;&nbsp;&nbsp; void SetDBAttrs(const tagDBAttrs&amp; rDBAttrs)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcpy(&amp;m_DBAttrs, rDBAttrs, sizeof(m_DBAttrs));<br>&nbsp;&nbsp;&nbsp; }  <p>private:<br>&nbsp;&nbsp;&nbsp; tagDBAttrs&nbsp;&nbsp;&nbsp; m_DBAttrs;<br>&nbsp;&nbsp;&nbsp; string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_strA;<br>&nbsp;&nbsp;&nbsp; CObject*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_pObjB;<br>&nbsp;&nbsp;&nbsp; CShape*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_pShapeC;<br>&nbsp;&nbsp;&nbsp; ...<br>};</code>&nbsp;</p> <p>之所以提到基本类型数据，主要缘于基本类型构成的结构可以通过<code>sizeof</code>运算符直接确定，而像类对象等组合类型，其深拷贝、赋值、操作及赋值等逻辑则较为复杂，一般无法统一处理。</p> <p>以上面的<code>tagDBAttrs</code>为例，对于基本类型数据处理具有非常大的优势，尤其在数据整体存取时，此外，增减基本类型属性也比较简单，而且不需要重写<code>Get/Set</code>接口，同时方便了对属性的分门别类处理。</p> <hr>  <p>此处顺便谈谈关于开发中的一些杂的体会。</p> <p>对于工作不久的同学而言，拿到一个任务有可能出现以下两种情况：</p> <p>1. 以快速开发为重，前期进展较快，在小型模块开发中顺风顺水，但在面对复杂任务时，因为对前期设计重视不足，后期会不断调整代码，甚至在基本功能开发结束后，因为逻辑架构问题，导致bug隐患较多较深，容易陷入泥沼，痛苦挣扎；</p> <p>2. 随时随处追求代码结构的优美和执行效率的优化，精益求精，往往在前期设计上花费过多的心思，以达到较合理的逻辑结构，甚至拖延整个项目的开发进度，这种同学一般说来，代码质量较高，后期bug较少。</p> <p>其实，上面说的就是我自己，我刚开始工作的时候，上面给我一个编写独立工具的任务，当时急于表现，缺乏项目实战经验，为编码而编码，逻辑结构不注重扩展性，设计不合理，在进行到后期时，面对出现的新需求无法应对，导致最后推到重来。</p> <p>后来，慢慢接受了注重前期设计的观念，更因为自己的完美情结和代码洁癖，不仅经常调整自己编码中不优美的片段，甚至越俎代庖去修改其他同事的代码，追求处处的高效，耗费大量时间精力，难以自拔。</p> <p>在任何一个项目中，都有轻重主次之分，也就是所谓的『<strong>8020原则</strong>』，关于『<strong>8020原则</strong>』怎么理解都行，我的理解就是，每个项目中的效率瓶颈是20%的核心代码，80%的时间和工作重心应该放到这20%的核心代码上。放到一个小的模块中也是这样，应该确定工作重心，在开发进度的限制下，对核心代码精益求精，对非效率瓶颈可以适当减少侧重。</p> <p>对于新人，最难的就是从整体的高度确定20%的工作重心，这主要依赖于自身素质的提高和开发经验的积累。而对需求深入细致的分析和对逻辑精益求精的设计本身可以训练一个人分析问题、解决问题的能力。</p> <p>总结一下的话，也正是我们项目组历来所倡导和坚持的：</p> <p>1. 精益求精；</p>2. 细节决定成败，态度制约能力。<img src ="http://www.cppblog.com/Fox/aggbug/70542.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-12-28 02:44 <a href="http://www.cppblog.com/Fox/archive/2008/12/28/70542.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><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>4</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>19</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>11</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>5</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>8</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></channel></rss>