• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Creative Commons License
            本Blog采用 知識共享署名-非商業性使用-禁止演繹 3.0 Unported許可協議 進行許可。 —— Fox <游戲人生>

            游戲人生

            游戲人生 != ( 人生 == 游戲 )
            站點遷移至:http://www.yulefox.com。請訂閱本博的朋友將RSS修改為http://feeds.feedburner.com/yulefox
            posts - 62, comments - 508, trackbacks - 0, articles - 7

            本文同時發布在

            近來在Windows下用WSAEventSelect時,碰到一個棘手的問題,當然現在已經解決了。

            問題描述:

            一個Server,一個ClientA,一個ClientB,Server用WSAEventSelect模型監聽(只有監聽,沒有讀寫),ClientA在連接Server后,ClientA對應的EventA被觸發,Server的WSAWaitForMultipleEvents等待到EventA,ClientB連接Server時,TCP三次握手成功,ClientB與Server的TCP狀態被置為ESTABLISHED,然而Server的WSAWaitForMultipleEvents沒有等待到EventB被觸發。

            用netstat看了一下,ClientB與Server的狀態是ESTABLISHED,此時如果ClientB退出,由于Server無法正常Close該連接,因此Server的狀態不是TIME_WAIT而是CLOSE_WAIT(持續2小時),Client的狀態是FIN_WAIT_2(持續10分鐘)。

            我嘗試將ClientA主動關閉后再次連接Server,Server的WSAWaitForMultipleEvents在wait到EventA之后,EventB此時也被觸發。

            開始一直以為問題的根源在于WSAEventSelect的使用上,畢竟,之前沒有系統寫過類似的代碼,難免懷疑到事件模型的使用上。多方查閱資料,最后還是沒有發現類似問題的解決方案。

            又跟了一上午之后,Kevin開始懷疑是多線程使用的問題,我看了一下,的確沒有對event的多線程操作進行處理,但因為在另一個應用中,使用了同樣的模塊,卻沒有該問題。最后考慮必要性時還是放棄了加臨界資源,無視多線程同步問題。Kevin本來勸我換個模型,但我固執的認為要做就把這事兒做好。因為下午還要回學校一趟,就想盡快搞定,畢竟因為這一塊已經把Kevin的進度拖了一周了,心下還是過意不去,而且隱約感覺到離問題的解決越來越近了。

            問題分析:

            在對著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到也就不難理解了。

            問題解決:

            說到底是因為當ThreadA阻塞在WSAWaitForMultipleEvents處之時,事件集的變更無法立即得到體現。如果允許上層應用隨時create或close一些event,則WSAWaitForMultipleEvents就不應該無限阻塞下去。

            因此最后的一個解決方法就是讓WSAWaitForMultipleEvents超時返回并Sleep一段時間,當WSAWaitForMultipleEvents再次進入時事件集得以更新。

            想了一下,另一個應用中之所以沒出現該問題也只是個巧合,因為該應用中ThreadB的兩次WSAEventSelect間隔很短,在ThreadA獲得時間片之前已經確定了事件集。

            說白了這也不是一個什么大問題,甚至談不上任何難度,但是因為之前對WSAEventSelect沒有一個清晰的概念,因此在發現和分析問題上花費了大量時間,加上在VS2005調試過程中,有個別文件更新時沒有被重新編譯,也耗費了很多無謂的時間,以至于我們都在考慮是不是要放棄IDE,因為我們確實太依賴IDE了,有些TX為了穩妥,每次都是“重新生成整個解決方案”,如果一個解決方案有幾千個文件、幾十萬行的代碼,估計重編一次也要花個幾分鐘吧。

            總結:

            1. netstat觀察的網絡連接處于ESTABLISHED狀態并不意味著邏輯連接被accept,只是表明客戶端connect的TCP物理連接(三次握手)被服務器端ack,如果服務器沒有accept到該連接,證明網絡模塊代碼有問題;
            2. 多線程怎么都是個問題,線程同步盡量避免,畢竟,用Kevin的話來說,加鎖是丑陋的。但在涉及到同步問題時,還是權衡一下,我這兒之所以最后沒有加臨界區,是因為事件主要是在ThreadA中處理,ThreadB中只有create操作,而且ThreadA對事件集的刷新要求不是那么嚴格,也就不考慮加臨界區了;
            3. 如果能力和條件允許的話,放棄IDE吧,IDE的確不是個好東西,我主要是指在編譯鏈接的時候,如果作為編輯器說不定還會好用:)。

            個人網站用的主機最近從據說要黑屏的Windows換成了Debian,還在調整,估計明天能弄好,內容肯定比Cppblog雜的多,談點技術的還是會同步更新到

            posted @ 2008-10-27 23:25 Fox 閱讀(5652) | 評論 (3)編輯 收藏

            作者:Fox

            本文同時發布在http://www.yulefox.comhttp://www.shnenglu.com/fox

            十天之前,在CPPBLOG上寫了一篇,有同學提到該實現不支持成員函數。這個問題我也考慮到了,既然被提出來,不妨把實現提供出來。

            需要說明的是,我本身對template比較不感冒,不過對template感冒,而且寫過關于成員函數指針的問題,想了很久,如果支持成員函數指針,不用模板是不行了。

            此處對成員函數的支持還不涉及對函數參數的泛化,因為我這個消息映射暫時不需要參數泛化,下面的代碼應該不需要過多的解釋了。

            #define REG_MSG_FUNC(nMsgType, MsgFunc) \
                CMsgRegister::RegisterCallFunc(nMsgType, MsgFunc);

            #define REG_MSG_MEM_FUNC(nMsgType, Obj, MsgFunc) \
                CMsgRegister::RegisterCallFunc(nMsgType, Obj, MsgFunc);

            class CBaseMessage;

            class CHandler
            {
            public:
                virtual int operator()(CBaseMessage* pMsg) = 0;
            };

            template<typename FuncType>
            class CDefHandler : public CHandler
            {
            public:
                CDefHandler(){}
                CDefHandler(FuncType &Func)
                    : m_Func(Func)
                {
                }

                virtual int operator()(CBaseMessage* pMsg)
                {
                    return m_Func(pMsg);
                }

            protected:
                FuncType    m_Func;
            };

            template<typename ObjType, typename FuncType>
            class CMemHandler : public CHandler
            {
            public:
                CMemHandler(){}
                CMemHandler(ObjType* pObj, FuncType Func)
                    : m_pObj(pObj)
                    , m_Func(Func)
                {
                }

                virtual int operator()(CBaseMessage* pMsg)
                {
                    return (m_pObj->*m_Func)(pMsg);
                }

            protected:
                FuncType    m_Func;
                ObjType*    m_pObj;
            };

            class CFunction
            {
            public:
                CFunction()
                    : m_pHandler(NULL)
                {
                }

                // 封裝(C函數或靜態成員函數)
                template<typename FuncType>
                CFunction( FuncType &Func )
                    : m_pHandler(new CDefHandler<FuncType>(Func))
                {
                }

                // 封裝(非靜態成員函數)
                template<typename ObjType, typename FuncType>
                CFunction( ObjType* pObj, FuncType Func )
                    : m_pHandler(new CMemHandler<ObjType, FuncType>(pObj, Func))
                {
                }

                virtual ~CFunction()
                {
                    DELETE_SAFE(m_pHandler);
                }

                    // 函數調用
                int operator()(CBaseMessage* pMsg)
                {
                    return (*m_pHandler)(pMsg);
                }

            private:
                CHandler    *m_pHandler;
            };

            typedef std::map<int, CFunction*> MSG_MAP;
            typedef MSG_MAP::iterator MSG_ITR;

            class CMsgRegister
            {
            public:
                // 注冊消息函數(C函數或靜態成員函數)
                template <typename FuncType>
                inline static void RegisterCallFunc(int nMsgType, FuncType &Func)
                {
                    CFunction *func = new CFunction(Func);
                    s_MsgMap[nMsgType] = func;
                }

                // 注冊消息函數(非靜態成員函數)
                template <typename ObjType, typename FuncType>
                inline static void RegisterCallFunc(int nMsgType, ObjType* pObj, FuncType Func)
                {
                    CFunction *func = new CFunction(pObj, Func);
                    s_MsgMap[nMsgType] = func;
                }

                // 執行消息
                inline static void RunCallFunc(int nMsgType, CBaseMessage* pMsg)
                {
                    MSG_ITR itr = s_MsgMap.find(nMsgType);
                    if( s_MsgMap.end() != itr )
                    {
                        (*itr->second)(pMsg);
                    }
                }

                static void ReleaseMsgMap()                // 釋放消息映射表
                {
                    MSG_ITR itr = s_MsgMap.begin();
                    while( itr != s_MsgMap.end() )
                    {
                        DELETE_SAFE(itr->second);
                        itr = s_MsgMap.erase(itr);
                    }
                }

            protected:
                static MSG_MAP            s_MsgMap;        // 消息映射表
            };

            不可否認,模板給了你更大的想象空間,很多東西,還是不要一味排斥的好:)。

            posted @ 2008-10-10 10:20 Fox 閱讀(2690) | 評論 (4)編輯 收藏

            作者:Fox

            本文同時發布在http://www.yulefox.comhttp://www.shnenglu.com/fox

            兩個多月之前,在CPPBLOG上寫過一篇關于游戲開發中的問題,主要該考慮的問題都已經說明,當時沒有實現這一塊。在最近一個模塊中,寫了一個非常簡單的寫日志的接口,接口的聲明大概是:

            void PutoutLog(const char *szFile, const char *szLog, ...);

            記錄的日志格式如下:

            1  2008-10-10-03:30:10.618 | projectpath/srcfile.cpp/function(30) : 哦嚯, 這兒出錯了(eno : 0x00100000).

            用到了__FILE__、__LINE__、__FUNCTION__幾個宏。

            基本滿足需要了,需要改進的地方我現在能想到的主要是:

            • 文件名是全路徑,沒有必要,只記錄文件名稱其實就夠了;
            • 沒有考慮寫日志時的線程同步問題;
            • 系統dump時的日志還是沒有辦法記錄;
            • 缺少足夠的、動態的上下文信息:調用堆棧、函數參數、系統運行參數;
            • 日志記錄到普通文本中,雖然記錄了時間、位置,還是不便于系統查看、查找、分析、挖掘。

            說白了,這所謂的基本滿足需要只是皮毛,因為最近在打理,有感于網頁數據庫技術的博大精深、美妙直觀,如果可以把日志用網頁數據庫作為讀寫的載體,豈不甚妙?

            隱約中感覺這種想法似曾相識,不識字只好亂翻書,果然在中發現有這樣一篇文章:一個基于HTML的日志和調試系統。有興趣的同學自己翻書吧:)。

            如果將更加豐富的信息寫入xml或php文件中,加入到數據庫,可以對數據進行分析、挖掘,并友好的顯示在瀏覽器中,這對于枯燥的debug過程,起碼增添了些許益處。

            然而,這又只是一個想法,或許在我手頭上的工作稍后告一段落的時候,我可以花精力研究一下這方面的東西。

            posted @ 2008-10-10 04:18 Fox 閱讀(1836) | 評論 (8)編輯 收藏

            項目中使用了消息通信機制,因為消息類型非常多,相應的,處理消息的地方代碼也非常多。

            自然而然想到MFC中的消息映射:

            創建一個缺省MFC框架程序的解決方案Test,在Test.h中看到以下內容:

            class Ctest_mfcApp : public CWinApp
            {
            public:
                Ctest_mfcApp();

            // 重寫
            public:
                virtual BOOL InitInstance();

            // 實現
                afx_msg void OnAppAbout();
                DECLARE_MESSAGE_MAP()
            };

             

            其中,最緊要的就是DECLARE_MESSAGE_MAP()這個宏,相關內容展開如下:

            struct AFX_MSGMAP_ENTRY
            {
                UINT nMessage;   // windows message
                UINT nCode;      // control code or WM_NOTIFY code
                UINT nID;        // control ID (or 0 for windows messages)
                UINT nLastID;    // used for entries specifying a range of control id's
                UINT_PTR nSig;   // signature type (action) or pointer to message #
                AFX_PMSG pfn;    // routine to call (or special value)
            };

            struct AFX_MSGMAP
            {
                const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
                const AFX_MSGMAP_ENTRY* lpEntries;
            };

            #define DECLARE_MESSAGE_MAP() \
            protected: \
                static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
                virtual const AFX_MSGMAP* GetMessageMap() const; \

            其中AFX_PMSG不再解析下去,我們認為這是一個指向特定消息對應的實現函數的函數指針,這幾個宏的作用可簡單理解成為Test這個項目定義了一個靜態的消息映射表。當消息到來時,從消息隊列中彈出消息并分發到具有入口實現的上層CWnd派生窗口。用戶只需要注冊消息,實現消息入口函數就夠了,這在MFC中一般放在.cpp文件頭部。Test.cpp中頭部有以下內容:

            BEGIN_MESSAGE_MAP(CTest, CWinApp)
                ON_COMMAND(ID_APP_ABOUT, &CTest::OnAppAbout)
                // 基于文件的標準文檔命令
                ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
                ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
                // 標準打印設置命令
                ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup)
            END_MESSAGE_MAP()

            這里是為消息枚舉值與消息實現函數建立映射,其中涉及到的宏的展開如下:

            #define ON_COMMAND(id, memberFxn) \
                { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
                    static_cast<AFX_PMSG> (memberFxn) },

            #define BEGIN_MESSAGE_MAP(theClass, baseClass) \
                PTM_WARNING_DISABLE \
                const AFX_MSGMAP* theClass::GetMessageMap() const \
                    { return GetThisMessageMap(); } \
                const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
                { \
                    typedef theClass ThisClass;                           \
                    typedef baseClass TheBaseClass;                       \
                    static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
                    {

            #define END_MESSAGE_MAP() \
                    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
                }; \
                    static const AFX_MSGMAP messageMap = \
                    { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
                    return &messageMap; \
                }                                  \
                PTM_WARNING_RESTORE

            按照上述思路得到的相似代碼如下:

            // Test.h
            typedef void (* funCall)(void*);        // 消息執行函數類型

            struct tagMsgEntry                      // 消息入口結構
            {
                int            nMsgType;            // 消息類型
                funCall        MsgRun;              // 消息執行函數
            };

            struct tagMsgMap                        // 消息映射表結構
            {
                const tagMsgMap* (__stdcall* funGetBaseMsgMap)();
                const tagMsgEntry* pMsgEntries;     // 消息入口集
            };

            class CMessage
            {
                // ...
            protected:
                static const tagMsgMap* __stdcall GetThisMsgMap();
                virtual const tagMsgMap* GetMsgMap() const;
            };

            // Test.cpp
            const tagMsgMap* CMessage::GetMsgMap() const
            {
                return GetThisMsgMap();
            }

            const tagMsgMap* __stdcall CMessage::GetThisMsgMap()
            {
                static const tagMsgEntry MsgEntries[] =
                {
                    { MSG_SOME_ONE, SomeOneFunc },
                    { MSG_SOME_TWO, SomeTwoFunc },
                    { MSG_NULL, NULL }
                };
                static const tagMsgMap msgMap =
                {
                    &CBaseMessage::GetThisMsgMap,    // 父類消息映射表
                    &MsgEntries[0]
                };
                return &msgMap;
            }

            int CMessage::MsgProc(int nMsgType)
            {
                switch( nMsgType )
                {
                case MSG_SOME_ONE:
                    {

                    }
                    break;
                }
                return CBaseMessage::MsgProc(nMsgType);
            }

            這種處理的優點在于,子類沒有定義的消息實現接口,可以使用父類接口實現。不過在現在的消息處理中,我們一般不需要由基類來完成,因此可以不依賴基類接口,使用宏可以使代碼看上去更加簡潔。

            ___________________________________________________________

            簡化版本的消息映射采用以下方式,簡單清晰:

            // Test.h
            #define REG_MSG_FUNC(nMsgType, MsgFunc) \
                CMessge::RegisterCallFunc(nMsgType, MsgFunc); \

            typedef void (* function)(void*);

            typedef std::map<int, function> MSG_MAP;
            typedef MSG_MAP::const_iterator MSG_CITR;

            class CMessage
            {
                // ...
            public:
                static const MSG_MAP& GetMsgMap();
                static void RegisterCallFunc(int nMsgType, void(* Func)(void *))
                {
                    s_MsgMap[nMsgType] = Func;
                }

                int CMessage::Run(int nMsgType)                // 消息公用執行函數
                {
                    MSG_ITR itr = s_MsgMap.find(nMsgType);
                    if( s_MsgMap.end() != itr )
                    {
                        itr->second(this);
                    }
                }

            protected:
                static MSG_MAP            s_MsgMap;            // 消息映射表
            };

            // UserTest.cpp -- 用戶在使用時對自己關心的消息予以注冊, 入口函數予以實現即可
            REG_MSG_FUNC(MSG_SOME_ONE, SomeOneFunc)

            void SomeOneFunc(CBaseMessage *pMsg)
            {
                return;
            }

            ___________________________________________________________

            最近忙的焦頭爛額,正好寫到消息,稍微整理一下,提供些許借鑒。

            posted @ 2008-09-29 18:34 Fox 閱讀(4506) | 評論 (31)編輯 收藏

            網絡編程學習和實踐的過程中,同步(synchronous)/異步(asynchronous)阻塞(blocking)/非阻塞(non-blocking)總是會迷惑很多人。依然記得我半年之前在記述IOCP時,一句不經意的“非阻塞I/O則是致力于提供高效的異步I/O”便引來一番口水論爭。

            今天在查一些資料的時候,看到關于這幾個詞的論辯竟不是一般的多,細細想來,這個問題似乎也確實有解釋的必要,不在于爭論對錯,而在于辨明是非。

            討論之前,先限定討論的范圍:此處之同步/異步僅限于I/O操作,與OS所討論的進程/線程中的其他同步/異步沒有直接關系;討論的內容是:兩對相似的術語之間的區別到底有多大

            • 非常大:

            Douglas C. Schmidt在《C++網絡編程》中這樣說到:

            They are very different, as follows:

            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.  When the operation completes there is some mechanism for the client to retrieve the results.

            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.

            翻譯如下:

            :例如,操作被異步調用時,控制權交給客戶端,I/O操作請求則交由操作系統內核處理,當操作完成后,通過某種機制將結果通知客戶端。

            非阻塞I/O:嘗試調用某操作,如果操作被阻塞,則調用返回-1并置錯誤值為EWOULDBLOCK。

            從這兩段“very different”的解釋來看,我的感覺是并沒有指出二者的區別,因為我們無法確定所謂AIO是如何處理的,如果AIO直接“調用返回-1并置錯誤值為EWOULDBLOCK”,實現“控制權交給客戶端”,似乎并無任何不妥。況且,對于非阻塞I/O,我們也需要“當操作完成后,通過某種機制將結果通知客戶端”這樣的處理。

            • 無差別:

            而在Wikipedia上則直接等同二者:Asynchronous I/O, or non-blocking I/O, is a form of input/output processing that permits other processing to continue before the transmission has finished.

            當然,對于recv和send,我們一般會說他們是阻塞起的,而不會說他們是同步起的,但這顯然不是二者的區別,因為我們都知道,阻塞的原因正是等待同步結果的返回

            因此,二者的區別在于,阻塞/非阻塞是表現,同步/異步是原因,我們說某某操作是阻塞起的,或者某某線程是阻塞起的,是因為在等待操作結果的同步返回;我們說某某操作是非阻塞的,是因為操作結果會通過異步方式返回。

            討論到這兒,再咬文嚼字的爭辯下去似乎已經沒有任何實際意義。

            ------------------------------------------------------------

            PS:糾結一些必要的概念是為了加深理解,太過糾結了反倒會滯塞理解。我之前對于其概念也并非特別清楚,所以才會再續一篇特意言明,也算彌補一下自己的過失。

            posted @ 2008-09-11 01:11 Fox 閱讀(4969) | 評論 (12)編輯 收藏

            When :  2008.8.8.20:00:00

            Where : National Stadium, Beijing, China.

            Who :    One World.

            What :   One Dream.

            All right, it is just a D-R-E-A-M...

            ____________________________

            但是,我一定會看,從電視上。

            就像看火炬一定要從電視上才和諧好些,開幕式也是。

            網上猜測李寧點火的人氣很高,之前官方也有透露說會有5.12汶川大地震相關內容,我猜想是這樣的:

            李寧身著Li-Ning牌黑白熊貓運動服,像功夫熊貓那樣,以體操功夫的糅合動作跳進8級地震中的深5.12m的名為汶川主火炬盆,以川劇中的吐火絕技點燃主火炬盆,高喊“我是李書記,快救我”,連做三個俯臥撐后,欲火浴火涅磐鳳凰,手提一瓶和諧醬油飛向太空……

            感覺很靠譜:D。

            posted @ 2008-08-08 14:16 Fox 閱讀(875) | 評論 (2)編輯 收藏

            0. Introduction

            接觸設計模式有兩年時間了,但一直沒有系統整理過,為了不至于讓自己的思維被繁瑣的工作一點點禁錮,還是決定總結一下,為了能夠真正做到有所收獲,整個系列會按照GoF的Design Patterns: Elements of Reusable Object-Oriented Software的行文思路,但不會照本宣科就是了,Wikipedia上關于23種設計模式的介紹非常全面,CSDN上也可以下載中/英文電子檔,因此很多套話、類圖一概省去。

            最早接觸設計模式的時候,難免被各種模式的聯系和區別所困擾,從教科書的分析可以得到模式之間形式上的不同。但這樣對于領會設計模式意義不大,因為我們掌握模式的目的是為了融會貫通,靈活運用,以對開發有所幫助。

            稍微成規模的OO程序,會有大量對象,其中很多實體對象之間存在著父子、兄弟關系,對象的創建提升為一種模式。其好處在于設計模式本身所宣稱的reusable,這就像堆積木蓋房子一樣,堆的好的情況下,換一換門窗便是另一番風景。

            關于實現,我不會為了厘清模式間的區別而刻意使用相似代碼實現,相反,我會根據模式本身的適用情況舉例,而且大量代碼基于SourceMaking

            _______________________________

            1. Creational Design Patterns(DP)

            創建型DP抽象了類和對象的創建過程,GoF給出了5種創建型DPAbstract FactoryBuilderFactory MethodBuilderPrototypeSingleton

            2. Abstract Factory

            意圖:提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

            1) 只提供了一個創建接口,其返回值為具體產品:如AbstractProduct *Client::CreateProduct(AbstractFactory &factory);

            2) 接口的參數是一個工廠對象AbstractFactory &factory)的引用,參數類型(AbstractFactory)為抽象基類,調用時根據需要傳入具體工廠對象即可;

            3) 接口內部實現了一系列相關或相互依賴對象(抽象產品)的創建:當傳入具體工廠時,接口實現的就是一系列具體產品的創建;

            4) 創建的產品立即返回CreateProduct)。

            參與者:

            • AbstractFactory
            — 聲明一個創建抽象產品對象的操作接口。

            • ConcreteFactory
            — 實現創建具體產品對象的操作。

            • AbstractProduct
            — 為一類產品對象聲明一個接口。

            • ConcreteProduct
            — 定義一個將被相應的具體工廠創建的產品對象。
            — 實現AbstractProduct接口。

            • Client
            — 僅使用由AbstractFactory和AbstractProduct類聲明的接口。

            代碼:

            class AbstractFactory
            {
            public:
                virtual AbstractProduct *MakePartA() = 0;
                virtual AbstractProduct *MakePartB() = 0;
                virtual AbstractProduct *MakePartC() = 0;
                virtual AbstractProduct *AddPart(const AbstractProduct *pPart) = 0;
            };

            AbstractProduct *Client::CreateProduct(AbstractFactory &factory)
            {
                AbstractProduct *pProduct = factory.CreateProduct();
                AbstractProduct *pPartA = factory.MakePartA();
                AbstractProduct *pPartB = factory.MakePartB();
                AbstractProduct *pPartC = factory.MakePartC();
                factory.AddPart(pPartA);
                factory.AddPart(pPartB);
                factory.AddPart(pPartC);
                return pProduct;
            }

            int main(void)
            {
                Client client;           
                ConcreteFactory factory;
                client.CreateProduct(factory);
                return 0;
            }

            3. Builder

            意圖:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

            1) director提供抽象產品創建接口:如void Director::Construct();

            2) 不同產品使用同一創建過程,由director指定特定builder以生產不同產品;

            3) 接口內部實現了一個復雜對象(抽象產品)的創建:當傳入具體工廠時,接口實現的是一個復雜的具體產品的創建;

            4) 創建的產品并不立即返回創建完畢后返回,或使用接口GetProduct)提取結果。

            參與者:

            • Builder
            — 為創建一個Product對象的各個部件指定抽象接口。

            • ConcreteBuilder
            — 實現Builder的接口以構造和裝配該產品的各個部件。
            — 定義并明確它所創建的表示。
            — 提供一個檢索產品的接口。

            • Director
            — 構造一個使用Builder接口的對象。

            • Product
            — 表示被構造的復雜對象。ConcreteBuilder創建該產品的內部表示并定義它的裝配過程。
            — 包含定義組成部件的類,包括將這些部件裝配成最終產品的接口。

            代碼:

            class Builder
            {
            public:
                virtual void MakePartA() = 0;
                virtual void MakePartB() = 0;
                virtual void MakePartC() = 0;

                Product *GetProduct()    { return _product; }

            protected:
                Product *_product;
            };

            class Director
            {
            public:
                void setBuilder(Builder *b)    { _builder = b; }
                void Construct();

            private:
                Builder *_builder;
            };

            void Director::Construct()
            {
                _builder.MakePartA();
                _builder.MakePartB();
                _builder.MakePartC();
            }

            int main(void) {
                ConcreteBuilderA concreteBuilderA;
                ConcreteBuilderB concreteBuilderB;
                Director director;
                Product *pProduct;

                director.SetBuilder(&concreteBuilderA);
                director.Construct();
                pProduct = concreteBuilderA.GetProduct();
                pProduct->Show();

                director.SetBuilder(&concreteBuilderB);
                director.Construct();
                pProduct = concreteBuilderB.GetProduct();
                pProduct->Show();

                return 0;
            }

            4. Factory Method

            意圖:定義一個用于創建對象的接口,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。

            1) 看得出該模式其實就是C++的多態特性,借繼承實現。因此,其別名為虛構造器( Virtual Constructor)

            2) 作為模式與C++多態特性不同的是,Creator可以定義工廠方法的缺省實現,完成缺省操作,MFC大量使用了這一思想。

            參與者:

            • Product
            — 定義工廠方法所創建的對象的接口。

            • ConcreteProduct
            — 實現Product接口。

            • Creator
            — 聲明工廠方法,該方法返回一個Product類型的對象。Creator也可以定義一個工廠方法的缺省實現,它返回一個缺省的ConcreteProduct對象。
            — 可以調用工廠方法以創建一個Product對象。

            • ConcreteCreator
            — 重定義工廠方法以返回一個ConcreteProduct實例。

            代碼:

            ConcreteProduct *ConcreteCreator::FactoryMethod()
            {
                ConcreteProduct *pProduct = new ConcreteProduct;
                return pProduct;
            }

            Product *Creator::FactoryMethod()
            {
                Product *pProduct = new Product;
                return pProduct;
            }

            int main(void) {
                Creator creator;
                ConcreteProduct *pProduct;

                pProduct = creator.FactoryMethod();
                pProduct->Show();

                return 0;
            }

            5. Prototype

            意圖:用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。

            1) 創建不再通過工廠新類繼承(inheritance),而是通過委托(delegation)

            2) 根通拷貝原型實例創建新對象。

            參與者:

            • ProtoType
            — 聲明一個克隆自身的接口。

            • ConcreteProtoType
            — 實現一個克隆自身的操作。

            • Client
            — 讓一個原型克隆自身從而創建一個新的對象。

            代碼:

            class ProtoType
            {
            public:
                virtual void Draw();
                virtual ProtoType *Clone() = 0;
                virtual void Initialize();
            };

            class ProtoTypeA: public ProtoType
            {
            public:
                virtual ProtoType *Clone()
                {
                    return new ProtoTypeA;
                }
            };

            class ProtoTypeB: public ProtoType
            {
            public:
                virtual ProtoType *Clone()
                {
                    return new ProtoTypeB;
                }
            };

            class Client
            {
            public:
                static ProtoType *Clone( int choice );

            private:
                static ProtoType *s_prototypes[3];
            };

            ProtoType* Client::s_prototypes[] = { 0, new ProtoTypeA, new ProtoTypeB };

            ProtoType *Client::Clone(int choice)
            {
                return s_prototypes[choice]->Clone();
            }

             

            6. Singleton

            意圖:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。

            1) 用靜態成員函數保證上述意圖。

            參與者:

            • Singleton
            — 定義一個Instance操作,允許客戶訪問它的唯一實例。Instance是一個類操作(即C++中的一個靜態成員函數)。
            — 可能負責創建它自己的唯一實例。

             

            代碼:

            class Singleton
            {
            public:
                static Singleton *GetInstance()
                {
                    if (!s_instance)
                        s_instance = new Singleton;
                    return s_instance;
                }

                void Run()    {}

            private:
                static Singleton *s_instance;
                Singleton()    {}                // Singleton cannot be created outside.
            };

            Singleton *GetSingleton(void)
            {
                return Singleton::GetInstance();
            }

            int main(void)
            {
                GetSingleton()->Run();

                return 0;
            }

            ______________________________________________

            代碼寫的都比較簡單,基本可以將各種模式之間的不同體現出來了。

            posted @ 2008-08-06 15:43 Fox 閱讀(2236) | 評論 (3)編輯 收藏

            一、Big-endian & Little-endian

            還是Wikipedia好啊!可惜中文的國內看不了,愚昧啊!實在覺得中文有點難懂,看看日本語版本吧:D!

            關于端(endianness)的介紹,Wikipedia上比較全了:http://en.wikipedia.org/wiki/Endianness

            關于網絡字節序(network byte order)主機字節序(host byte order),說來挺無關緊要的一點東西,因為每次總是忘掉,所以每次都要好奇的看看大端(big-endian)小端(little-endian)

            給定unsigned long型整數十六進制形式:0x0A0B0C0D,其big-endian和little-endian形式分別為:

            1) Big-endian

            Memory
            |
            ...  |  8-bit atomic element size       | ...    |  16-bit atomic element size
            | 0x0A |  a                               | 0x0A0B |  a
            | 0x0B |  a+1                             | 0x0C0D |  a+1
            | 0x0C |  a+2
            | 0x0D |  a+3
            | ...  |

            2) Little-endian(X86)

            Memory
            |
            ...  |  8-bit atomic element size       | ...    |  16-bit atomic element size
            | 0x0D |  a                               | 0x0C0D |  a
            | 0x0C |  a+1                             | 0x0A0B |  a+1
            | 0x0B |  a+2
            | 0x0A |  a+3
            | ...  |

            Mapping registers to memory locations (from Wikipedia)

            為什么X86存儲會使用little-endian,起初我想對于位運算,尤其是位移運算,little-endian很方便,但轉念一想,big-endian也方便啊,無非是左移和右移的區別而已,但little-endian的優勢在于unsigned char/short/int/long類型轉換時,存儲位置無需改變。

            在網絡傳輸中,采用big-endian序,對于0x0A0B0C0D,傳輸順序就是0A 0B 0C 0D,因此big-endian作為network byte order,little-endian作為host byte order。

            ________________________________________________

            PS:做雞有什么不好?

            上午跟某同事(為尊重慮,下文以Y稱之)躲在犄角旮旯抽煙。以下為場景再現:

            (忽然整出來一句)Y:聽過鷹的故事沒有?

            (滿臉疑惑)Fox:沒有。

            Y:一只小鷹掉到雞窩里,#$@%……

            F:我不是鷹,我就是一只雞,做技術雞有什么不好?

            Y:做技術沒有不好啊……

            F:我不是說做技術,我說做雞,我就是在地上走的,我為什么總是要抬頭看天?

            Y:你要往上看,沒有人注定不能飛,XX以前也沒有想過有一天會飛起來。

            F:我不是掉到雞窩里,我本來就在雞窩里,我也喜歡呆在雞窩里,別人都在地上走,我為什么要飛起來?

            Y:你總要飛起來。

            F:我說了我喜歡呆在雞窩里,你見過有那只雞飛起來了?

            Y:……

            F:我就是一只雞,插了雞翅還是飛不起來,況且,我對飛起來也沒有任何興趣。

            Y:……

            F:做雞有什么不好?

            Y:你看老毛,與人斗其樂無窮,他境界多高,與天斗其樂無窮,知道吧,他已經不屑與人斗了。

            F:我不喜歡與人斗,我也斗不過,做雞有什么不好?

            Y:……

            posted @ 2008-07-30 14:48 Fox 閱讀(2036) | 評論 (4)編輯 收藏

            原文地址:

            • 規則之例外

            前面說明的編碼習慣基本是強制性的,但所有優秀的規則都允許例外。

            1. 現有不統一代碼(Existing Non-conformant Code)

            對于現有不符合既定編程風格的代碼可以網開一面。

            當你修改使用其他風格的代碼時,為了與代碼原有風格保持一致可以不使用本指南約定。如果不放心可以與代碼原作者或現在的負責人員商討,記住,一致性包括原有的一致性。

            1. Windows代碼(Windows Code)

            Windows程序員有自己的編碼習慣,主要源于Windows的一些頭文件和其他Microsoft代碼。我們希望任何人都可以順利讀懂你的代碼,所以針對所有平臺的C++編碼給出一個單獨的指導方案。

            如果你一直使用Windows編碼風格的,這兒有必要重申一下某些你可能會忘記的指南(譯者注,我怎么感覺像在被洗腦:D)

            1) 不要使用匈牙利命名法(Hungarian notation,如定義整型變量為iNum,使用Google命名約定,包括對源文件使用.cc擴展名;

            2) Windows定義了很多原有內建類型的同義詞(譯者注,這一點,我也很反感),如DWORDHANDLE等等,在調用Windows API時這是完全可以接受甚至鼓勵的,但還是盡量使用原來的C++類型,例如,使用const TCHAR *而不是LPCTSTR

            3) 使用Microsoft Visual C++進行編譯時,將警告級別設置為3或更高,并將所有warnings當作errors處理

            4) 不要使用#pragma once;作為包含保護,使用C++標準包含保護包含保護的文件路徑包含到項目樹頂層(譯者注,#include<prj_name/public/tools.h>

            5) 除非萬不得已,否則不使用任何不標準的擴展,如#pragma__declspec,允許使用__declspec(dllimport)__declspec(dllexport),但必須通過DLLIMPORTDLLEXPORT等宏,以便其他人在共享使用這些代碼時容易放棄這些擴展。

            在Windows上,只有很少一些偶爾可以不遵守的規則:

            1) 通常我們禁止使用多重繼承,但在使用COMATL/WTL類時可以使用多重繼承,為了執行COMATL/WTL類及其接口時可以使用多重實現繼承;

            2) 雖然代碼中不應使用異常,但在ATL和部分STL(包括Visual C++的STL)中異常被廣泛使用,使用ATL時,應定義_ATL_NO_EXCEPTIONS以屏蔽異常,你要研究一下是否也屏蔽掉STL的異常,如果不屏蔽,開啟編譯器異常也可以,注意這只是為了編譯STL,自己仍然不要寫含異常處理的代碼;

            3) 通常每個項目的每個源文件中都包含一個名為StdAfx.hprecompile.h的頭文件方便頭文件預編譯,為了使代碼方便與其他項目共享,避免顯式包含此文件(precompile.cc除外),使用編譯器選項/FI以自動包含;

            4) 通常名為resource.h、且只包含宏的資源頭文件,不必拘泥于此風格指南。

            • 團隊合作

            參考常識,保持一致

            編輯代碼時,花點時間看看項目中的其他代碼并確定其風格,如果其他代碼if語句中使用空格,那么你也要使用。如果其中的注釋用星號(*)圍成一個盒子狀,你也這樣做:

            /**********************************
            * Some comments are here.
            * There may be many lines.
            **********************************/
            
            

            編程風格指南的使用要點在于提供一個公共的編碼規范,所有人可以把精力集中在實現內容而不是表現形式上。我們給出了全局的風格規范,但局部的風格也很重要,如果你在一個文件中新加的代碼和原有代碼風格相去甚遠的話,這就破壞了文件本身的整體美觀也影響閱讀,所以要盡量避免。

            好了,關于編碼風格寫的差不多了,代碼本身才是更有趣的,盡情享受吧!

            Benjy Weinberger
            Craig Silverstein
            Gregory Eitzmann
            Mark Mentovai
            Tashana Landray

            ______________________________________

            譯者:終于翻完了,前后歷時兩周,整個過程中,雖因工作關系偶有懈怠,但總算不是虎頭蛇尾(起碼我的態度是非常認真的:D),無論是否能對你有所裨益,對我而言,至少是溫習了一些以前知道的知識,也學到了一些之前不知道的知識

            剛好這兩天還不是特緊張,趕緊翻完了,要開始干活了……

            posted @ 2008-07-23 14:28 Fox 閱讀(3836) | 評論 (11)編輯 收藏

            原文地址:

            • 格式

            代碼風格和格式確實比較隨意,但一個項目中所有人遵循同一風格是非常容易的,作為個人未必同意下述格式規則的每一處,但整個項目服從統一的編程風格是很重要的,這樣做才能讓所有人在閱讀和理解代碼時更加容易。

            1. 行長度(Line Length)

            每一行代碼字符數不超過80。

            我們也認識到這條規則是存有爭議的,但如此多的代碼都遵照這一規則,我們感覺一致性更重要。

            優點:提倡該原則的人認為強迫他們調整編輯器窗口大小很野蠻。很多人同時并排開幾個窗口,根本沒有多余空間拓寬某個窗口,人們將窗口最大尺寸加以限定,一致使用80列寬,為什么要改變呢?

            缺點:反對該原則的人則認為更寬的代碼行更易閱讀,80列的限制是上個世紀60年代的大型機的古板缺陷;現代設備具有更寬的顯示屏,很輕松的可以顯示更多代碼。

            結論:80個字符是最大值。例外:

            1) 如果一行注釋包含了超過80字符的命令或URL,出于復制粘貼的方便可以超過80字符;

            2) 包含長路徑的可以超出80列,盡量避免;

            3) 頭文件保護(防止重復包含第一篇)可以無視該原則。

            2. 非ASCII字符(Non-ASCII Characters)

            盡量不使用非ASCII字符,使用時必須使用UTF-8格式。

            哪怕是英文,也不應將用戶界面的文本硬編碼到源代碼中,因此非ASCII字符要少用。特殊情況下可以適當包含此類字符,如,代碼分析外部數據文件時,可以適當硬編碼數據文件中作為分隔符的非ASCII字符串;更常用的是(不需要本地化的)單元測試代碼可能包含非ASCII字符串。此類情況下,應使用UTF-8格式,因為很多工具都可以理解和處理其編碼,十六進制編碼也可以,尤其是在增強可讀性的情況下——如"\xEF\xBB\xBF"是Unicode的zero-width no-break space字符,以UTF-8格式包含在源文件中是不可見的。

            3. 空格還是制表位(Spaces vs. Tabs)

            只使用空格,每次縮進2個空格。

            使用空格進行縮進,不要在代碼中使用tabs,設定編輯器將tab轉為空格。

            譯者注:在前段時間的關于Debian開發學習日記一文中,曾給出針對C/C++編碼使用的vim配置。

            4. 函數聲明與定義(Function Declarations and Definitions)

            返回類型和函數名在同一行,合適的話,參數也放在同一行。

            函數看上去像這樣:

            ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
              DoSomething();
              ...
            }
            
            

            如果同一行文本較多,容不下所有參數:

            ReturnType ClassName::ReallyLongFunctionName(Type par_name1,
                                                         Type par_name2,
                                                         Type par_name3) {
              DoSomething();
              ...
            }
            
            

            甚至連第一個參數都放不下:

            ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
                Type par_name1,  // 4 space indent
                Type par_name2,
                Type par_name3) {
              DoSomething();  // 2 space indent
              ...
            }
            
            

            注意以下幾點:

            1) 返回值總是和函數名在同一行;

            2) 左圓括號(open parenthesis)總是和函數名在同一行;

            3) 函數名和左圓括號間沒有空格;

            4) 圓括號與參數間沒有空格;

            5) 左大括號(open curly brace)總在最后一個參數同一行的末尾處;

            6) 右大括號(close curly brace)總是單獨位于函數最后一行;

            7) 右圓括號(close parenthesis)和左大括號間總是有一個空格;

            8) 函數聲明和實現處的所有形參名稱必須保持一致;

            9) 所有形參應盡可能對齊;

            10) 缺省縮進為2個空格;

            11) 獨立封裝的參數保持4個空格的縮進。

            如果函數為const的,關鍵字const應與最后一個參數位于同一行。

            // 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 {
              ...
            }
            
            

            如果有些參數沒有用到,在函數定義處將參數名注釋起來:

            // 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*/) {}
            // Bad - if someone wants to implement later, it's not clear what the
            // variable means.
            void Circle::Rotate(double) {}
            
            

            譯者注:關于UNIX/Linux風格為什么要把左大括號置于行尾(.cc文件的函數實現處,左大括號位于行首),我的理解是代碼看上去比較簡約,想想行首除了函數體被一對大括號封在一起之外,只有右大括號的代碼看上去確實也舒服;Windows風格將左大括號置于行首的優點是匹配情況一目了然。

            5. 函數調用(Function Calls)

            盡量放在同一行,否則,將實參封裝在圓括號中。

            函數調用遵循如下形式:

            bool retval = DoSomething(argument1, argument2, argument3);
            
            

            如果同一行放不下,可斷為多行,后面每一行都和第一個實參對齊,左圓括號后和右圓括號前不要留空格:

            bool retval = DoSomething(averyveryveryverylongargument1,
                                      argument2, argument3);
            
            

            如果函數參數比較多,可以出于可讀性的考慮每行只放一個參數:

            bool retval = DoSomething(argument1,
                                      argument2,
                                      argument3,
                                      argument4);
            
            

            如果函數名太長,以至于超過行最大長度,可以將所有參數獨立成行:

            if (...) {
              ...
              ...
              if (...) {
                DoSomethingThatRequiresALongFunctionName(
                    very_long_argument1,  // 4 space indent
                    argument2,
                    argument3,
                    argument4);
              }
            
            

            6. 條件語句(Conditionals)

            更提倡不在圓括號中添加空格,關鍵字else另起一行。

            對基本條件語句有兩種可以接受的格式,一種在圓括號和條件之間有空格,一種沒有。

            最常見的是沒有空格的格式,那種都可以,還是一致性為主。如果你是在修改一個文件,參考當前已有格式;如果是寫新的代碼,參考目錄下或項目中其他文件的格式,還在徘徊的話,就不要加空格了。

            if (condition) {  // no spaces inside parentheses
              ...  // 2 space indent.
            } else {  // The else goes on the same line as the closing brace.
              ...
            }
            
            

            如果你傾向于在圓括號內部加空格:

            if ( condition ) {  // spaces inside parentheses - rare
              ...  // 2 space indent.
            } else {  // The else goes on the same line as the closing brace.
              ...
            }
            
            

            注意所有情況下if和左圓括號間有個空格,右圓括號和左大括號(如果使用的話)間也要有個空格:

            if(condition)     // Bad - space missing after IF.
            if (condition){   // Bad - space missing before {.
            if(condition){    // Doubly bad.
            if (condition) {  // Good - proper space after IF and before {.
            
            

            有些條件語句寫在同一行以增強可讀性,只有當語句簡單并且沒有使用else子句時使用:

            if (x == kFoo) return new Foo();
            if (x == kBar) return new Bar();
            
            

            如果語句有else分支是不允許的:

            // Not allowed - IF statement on one line when there is an ELSE clause
            if (x) DoThis();
            else DoThat();
            
            

            通常,單行語句不需要使用大括號,如果你喜歡也無可厚非,也有人要求if必須使用大括號:

            if (condition)
              DoSomething();  // 2 space indent.
            
            if (condition) {
              DoSomething();  // 2 space indent.
            }
            
            

            但如果語句中哪一分支使用了大括號的話,其他部分也必須使用:

            // 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;
            }
             
            // Curly braces around both IF and ELSE required because
            // one of the clauses used braces.
            if (condition) {
              foo;
            } else {
              bar;
            }
            
            

            7. 循環和開關選擇語句(Loops and Switch Statements)

            switch語句可以使用大括號分塊;空循環體應使用{}continue

            switch語句中的case塊可以使用大括號也可以不用,取決于你的喜好,使用時要依下文所述。

            如果有不滿足case枚舉條件的值,要總是包含一個default(如果有輸入值沒有case去處理,編譯器將報警)。如果default永不會執行,可以簡單的使用assert

            switch (var) {
              case 0: {  // 2 space indent
                ...      // 4 space indent
                break;
              }
              case 1: {
                ...
                break;
              }
              default: {
                assert(false);
              }
            }
            
            

            空循環體應使用{}continue,而不是一個簡單的分號:

            while (condition) {
              // Repeat test until it returns false.
            }
            for (int i = 0; i < kSomeNumber; ++i) {}  // Good - empty body.
            while (condition) continue;  // Good - continue indicates no logic.
            while (condition);  // Bad - looks like part of do/while loop.
            
            

            8. 指針和引用表達式(Pointers and Reference Expressions)

            句點(.)或箭頭(->)前后不要有空格,指針/地址操作符(*、&)后不要有空格。

            下面是指針和引用表達式的正確范例:

            x = *p;
            p = &x;
            x = r.y;
            x = r->y;
            
            

            注意:

            1) 在訪問成員時,句點或箭頭前后沒有空格;

            2) 指針操作符*&后沒有空格。

            在聲明指針變量或參數時,星號與類型或變量名緊挨都可以:

            // These are fine, space preceding.
            char *c;
            const string &str;
            
            // These are fine, space following.
            char* c;    // but remember to do "char* c, *d, *e, ...;"!
            const string& str;
            char * c;  // Bad - spaces on both sides of *
            const string & str;  // Bad - spaces on both sides of &
            
            

            同一個文件(新建或現有)中起碼要保持一致。

            譯者注:個人比較習慣與變量緊挨的方式

            9. 布爾表達式(Boolean Expressions)

            如果一個布爾表達式超過標準行寬(80字符),如果斷行要統一一下。

            下例中,邏輯與(&&)操作符總位于行尾:

            if (this_one_thing > this_other_thing &&
                a_third_thing == a_fourth_thing &&
                yet_another & last_one) {
              ...
            }
            
            

            兩個邏輯與(&&)操作符都位于行尾,可以考慮額外插入圓括號,合理使用的話對增強可讀性是很有幫助的。

            譯者注:個人比較習慣邏輯運算符位于行首,邏輯關系一目了然,各人喜好而已,至于加不加圓括號的問題,如果你對優先級了然于胸的話可以不加,但可讀性總是差了些

            10. 函數返回值(Return Values)

            return表達式中不要使用圓括號。

            函數返回時不要使用圓括號:

            return x;  // not return(x);
            
            

            11. 變量及數組初始化(Variable and Array Initialization)

            選擇=還是()

            需要做二者之間做出選擇,下面的形式都是正確的:

            int x = 3;
            int x(3);
            string name("Some Name");
            string name = "Some Name";
            
            

            12. 預處理指令(Preprocessor Directives)

            預處理指令不要縮進,從行首開始。

            即使預處理指令位于縮進代碼塊中,指令也應從行首開始。

            // Good - directives at beginning of line
              if (lopsided_score) {
            #if DISASTER_PENDING      // Correct -- Starts at beginning of line
                DropEverything();
            #endif
                BackToNormal();
              }
            // 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();
              }
            
            

            13. 類格式(Class Format)

            聲明屬性依次序是public:protected:private:,每次縮進1個空格(譯者注,為什么不是兩個呢?也有人提倡private在前,對于聲明了哪些數據成員一目了然,還有人提倡依邏輯關系將變量與操作放在一起,都有道理:-)

            類聲明(對類注釋不了解的話,參考第六篇中的類注釋一節)的基本格式如下:

            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);
            };
            
            

            注意:

            1) 所以基類名應在80列限制下盡量與子類名放在同一行;

            2) 關鍵詞public:、protected:private:要縮進1個空格(譯者注,MSVC多使用tab縮進,且這三個關鍵詞沒有縮進)

            3) 除第一個關鍵詞(一般是public)外,其他關鍵詞前空一行,如果類比較小的話也可以不空;

            4) 這些關鍵詞后不要空行;

            5) public放在最前面,然后是protectedprivate

            6) 關于聲明次序參考第三篇聲明次序一節。

            14. 初始化列表(Initializer Lists)

            構造函數初始化列表放在同一行或按四格縮進并排幾行。

            兩種可以接受的初始化列表格式:

            // When it all fits on one line:
            MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {
            
            

            // 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();
              ...
            }
            
            

            15. 命名空間格式化(Namespace Formatting)

            命名空間內容不縮進。

            命名空間不添加額外縮進層次,例如:

            namespace {
            
            void foo() {  // Correct.  No extra indentation within namespace.
              ...
            }
            
            }  // namespace
            
            

            不要縮進:

            namespace {
            
              // Wrong.  Indented when it should not be.
              void foo() {
                ...
              }
            
            }  // namespace
            
            

            16. 水平留白(Horizontal Whitespace)

            水平留白的使用因地制宜。不要在行尾添加無謂的留白。

            普通

            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.
              ...
            
            

            添加冗余的留白會給其他人編輯時造成額外負擔,因此,不要加入多余的空格。如果確定一行代碼已經修改完畢,將多余的空格去掉;或者在專門清理空格時去掉(確信沒有其他人在使用)。

            循環和條件語句

            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 < 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 < 5; ++i ) {
            for ( ; i < 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.
            
            

            操作符

            x = 0;              // Assignment operators always have spaces around
                                // them.
            x = -5;             // No spaces separating unary operators and their
            ++x;                // arguments.
            if (x && !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.
            
            

            模板和轉換

            vector<string> x;           // No spaces inside the angle
            y = static_cast<char*>(x);  // brackets (< and >), before
                                        // <, or between >( in a cast.
            vector<char *> x;           // Spaces between type and pointer are
                                        // okay, but be consistent.
            set<list<string> > x;       // C++ requires a space in > >.
            set< list<string> > x;      // You may optionally make use
                                        // symmetric spacing in < <.
            
            

            17. 垂直留白(Vertical Whitespace)

            垂直留白越少越好。

            這不僅僅是規則而是原則問題了:不是非常有必要的話就不要使用空行。尤其是:不要在兩個函數定義之間空超過2行,函數體頭、尾不要有空行,函數體中也不要隨意添加空行。

            基本原則是:同一屏可以顯示越多的代碼,程序的控制流就越容易理解。當然,過于密集的代碼塊和過于疏松的代碼塊同樣難看,取決于你的判斷,但通常是越少越好。

            函數頭、尾不要有空行:

            void Function() {
            
              // Unnecessary blank lines before and after
            
            }
            
            

            代碼塊頭、尾不要有空行:

            while (condition) {
              // Unnecessary blank line after
            
            }
            if (condition) {
            
              // Unnecessary blank line before
            }
            
            

            if-else塊之間空一行還可以接受:

            if (condition) {
              // Some lines of code too small to move to another function,
              // followed by a blank line.
            
            } else {
              // Another block of code
            }
            
            

            ______________________________________

            譯者:首先說明,對于代碼格式,因人、因系統各有優缺點,但同一個項目中遵循同一標準還是有必要的:

            1. 行寬原則上不超過80列,把22寸的顯示屏都占完,怎么也說不過去;

            2. 盡量不使用非ASCII字符,如果使用的話,參考UTF-8格式(尤其是UNIX/Linux下,Windows下可以考慮寬字符),盡量不將字符串常量耦合到代碼中,比如獨立出資源文件,這不僅僅是風格問題了;

            3. UNIX/Linux下無條件使用空格,MSVC的話使用Tab也無可厚非;

            4. 函數參數、邏輯條件、初始化列表:要么所有參數和函數名放在同一行,要么所有參數并排分行;

            5. 除函數定義的左大括號可以置于行首外,包括函數/類/結構體/枚舉聲明、各種語句的左大括號置于行尾,所有右大括號獨立成行;

            6. ./->操作符前后不留空格,*/&不要前后都留,一個就可,靠左靠右依各人喜好;

            7. 預處理指令/命名空間不使用額外縮進,類/結構體/枚舉/函數/語句使用縮進;

            8. 初始化用=還是()依個人喜好,統一就好;

            9. return不要加();

            10. 水平/垂直留白不要濫用,怎么易讀怎么來。

            posted @ 2008-07-23 11:43 Fox 閱讀(4400) | 評論 (7)編輯 收藏

            僅列出標題
            共7頁: 1 2 3 4 5 6 7 
            91超碰碰碰碰久久久久久综合| 欧美国产成人久久精品| 久久伊人精品一区二区三区| 亚洲国产香蕉人人爽成AV片久久 | 国内精品久久久久影院网站| 久久精品这里热有精品| 久久国产精品免费一区二区三区 | 久久久久免费视频| 色综合久久无码五十路人妻 | 久久亚洲精品无码aⅴ大香| 亚洲级αV无码毛片久久精品| 精品亚洲综合久久中文字幕| 久久人搡人人玩人妻精品首页| 久久精品国产99久久久古代 | 免费久久人人爽人人爽av| 精品久久777| 成人久久免费网站| 久久久久久无码国产精品中文字幕| 久久SE精品一区二区| 欧美性猛交xxxx免费看久久久| 欧洲成人午夜精品无码区久久 | 狠狠干狠狠久久| 亚洲va久久久噜噜噜久久天堂| 久久天天躁狠狠躁夜夜2020老熟妇| 91精品国产色综合久久| 97久久婷婷五月综合色d啪蜜芽| 久久国产视屏| 伊人久久大香线焦综合四虎| 久久久久99精品成人片试看| 亚洲国产另类久久久精品| 尹人香蕉久久99天天拍| 久久一区二区三区99| 久久亚洲天堂| 久久夜色精品国产| 欧美色综合久久久久久| 国产成人精品久久综合| 国产精品免费久久久久久久久| 久久精品人成免费| 久久久中文字幕| 久久久久国产精品嫩草影院| 久久久精品久久久久久|