青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Creative Commons License
本Blog采用 知識(shí)共享署名-非商業(yè)性使用-禁止演繹 3.0 Unported許可協(xié)議 進(jìn)行許可。 —— Fox <游戲人生>

游戲人生

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

本文同時(shí)發(fā)布在

近來在Windows下用WSAEventSelect時(shí),碰到一個(gè)棘手的問題,當(dāng)然現(xiàn)在已經(jīng)解決了。

問題描述:

一個(gè)Server,一個(gè)ClientA,一個(gè)ClientB,Server用WSAEventSelect模型監(jiān)聽(只有監(jiān)聽,沒有讀寫),ClientA在連接Server后,ClientA對(duì)應(yīng)的EventA被觸發(fā),Server的WSAWaitForMultipleEvents等待到EventA,ClientB連接Server時(shí),TCP三次握手成功,ClientB與Server的TCP狀態(tài)被置為ESTABLISHED,然而Server的WSAWaitForMultipleEvents沒有等待到EventB被觸發(fā)。

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

我嘗試將ClientA主動(dòng)關(guān)閉后再次連接Server,Server的WSAWaitForMultipleEvents在wait到EventA之后,EventB此時(shí)也被觸發(fā)。

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

又跟了一上午之后,Kevin開始懷疑是多線程使用的問題,我看了一下,的確沒有對(duì)event的多線程操作進(jìn)行處理,但因?yàn)樵诹硪粋€(gè)應(yīng)用中,使用了同樣的模塊,卻沒有該問題。最后考慮必要性時(shí)還是放棄了加臨界資源,無視多線程同步問題。Kevin本來勸我換個(gè)模型,但我固執(zhí)的認(rèn)為要做就把這事兒做好。因?yàn)橄挛邕€要回學(xué)校一趟,就想盡快搞定,畢竟因?yàn)檫@一塊已經(jīng)把Kevin的進(jìn)度拖了一周了,心下還是過意不去,而且隱約感覺到離問題的解決越來越近了。

問題分析:

在對(duì)著WSAWaitForMultipleEvents思考了半天之后,忽然開竅了,如果ThreadA在WSAWaitForMultipleEvents時(shí),只有一個(gè)EventA被WSAEventSelect并set到signaled狀態(tài),則該EventA會(huì)被wait成功,ThreadA處理EventA之后繼續(xù)阻塞在WSAWaitForMultipleEvents。此時(shí),ThreadB通過WSAEventSelect將EventB初始化為nonsignaled狀態(tài),之后即使EventB被set為signaled狀態(tài),但ThreadA的WSAWaitForMultipleEvents因?yàn)樘幱谧枞麪顟B(tài),不可能刷新事件集,也就不可能wait到EventB,最終導(dǎo)致了ClientB的請(qǐng)求無法被響應(yīng)。如果EventA被觸發(fā)則會(huì)被ThreadA等待到,WSAWaitForMultipleEvents返回后再次進(jìn)入時(shí)事件集已經(jīng)被刷新,EventB被wait到也就不難理解了。

問題解決:

說到底是因?yàn)楫?dāng)ThreadA阻塞在WSAWaitForMultipleEvents處之時(shí),事件集的變更無法立即得到體現(xiàn)。如果允許上層應(yīng)用隨時(shí)create或close一些event,則WSAWaitForMultipleEvents就不應(yīng)該無限阻塞下去。

因此最后的一個(gè)解決方法就是讓W(xué)SAWaitForMultipleEvents超時(shí)返回并Sleep一段時(shí)間,當(dāng)WSAWaitForMultipleEvents再次進(jìn)入時(shí)事件集得以更新。

想了一下,另一個(gè)應(yīng)用中之所以沒出現(xiàn)該問題也只是個(gè)巧合,因?yàn)樵搼?yīng)用中ThreadB的兩次WSAEventSelect間隔很短,在ThreadA獲得時(shí)間片之前已經(jīng)確定了事件集。

說白了這也不是一個(gè)什么大問題,甚至談不上任何難度,但是因?yàn)橹皩?duì)WSAEventSelect沒有一個(gè)清晰的概念,因此在發(fā)現(xiàn)和分析問題上花費(fèi)了大量時(shí)間,加上在VS2005調(diào)試過程中,有個(gè)別文件更新時(shí)沒有被重新編譯,也耗費(fèi)了很多無謂的時(shí)間,以至于我們都在考慮是不是要放棄IDE,因?yàn)槲覀兇_實(shí)太依賴IDE了,有些TX為了穩(wěn)妥,每次都是“重新生成整個(gè)解決方案”,如果一個(gè)解決方案有幾千個(gè)文件、幾十萬行的代碼,估計(jì)重編一次也要花個(gè)幾分鐘吧。

總結(jié):

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

個(gè)人網(wǎng)站用的主機(jī)最近從據(jù)說要黑屏的Windows換成了Debian,還在調(diào)整,估計(jì)明天能弄好,內(nèi)容肯定比Cppblog雜的多,談點(diǎn)技術(shù)的還是會(huì)同步更新到

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

作者:Fox

本文同時(shí)發(fā)布在http://www.yulefox.comhttp://m.shnenglu.com/fox

十天之前,在CPPBLOG上寫了一篇,有同學(xué)提到該實(shí)現(xiàn)不支持成員函數(shù)。這個(gè)問題我也考慮到了,既然被提出來,不妨把實(shí)現(xiàn)提供出來。

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

此處對(duì)成員函數(shù)的支持還不涉及對(duì)函數(shù)參數(shù)的泛化,因?yàn)槲疫@個(gè)消息映射暫時(shí)不需要參數(shù)泛化,下面的代碼應(yīng)該不需要過多的解釋了。

#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函數(shù)或靜態(tài)成員函數(shù))
    template<typename FuncType>
    CFunction( FuncType &Func )
        : m_pHandler(new CDefHandler<FuncType>(Func))
    {
    }

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

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

        // 函數(shù)調(diào)用
    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è)消息函數(shù)(C函數(shù)或靜態(tài)成員函數(shù))
    template <typename FuncType>
    inline static void RegisterCallFunc(int nMsgType, FuncType &Func)
    {
        CFunction *func = new CFunction(Func);
        s_MsgMap[nMsgType] = func;
    }

    // 注冊(cè)消息函數(shù)(非靜態(tài)成員函數(shù))
    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;
    }

    // 執(zhí)行消息
    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;        // 消息映射表
};

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

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

作者:Fox

本文同時(shí)發(fā)布在http://www.yulefox.comhttp://m.shnenglu.com/fox

兩個(gè)多月之前,在CPPBLOG上寫過一篇關(guān)于游戲開發(fā)中的問題,主要該考慮的問題都已經(jīng)說明,當(dāng)時(shí)沒有實(shí)現(xiàn)這一塊。在最近一個(gè)模塊中,寫了一個(gè)非常簡(jiǎn)單的寫日志的接口,接口的聲明大概是:

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

記錄的日志格式如下:

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

用到了__FILE__、__LINE__、__FUNCTION__幾個(gè)宏。

基本滿足需要了,需要改進(jìn)的地方我現(xiàn)在能想到的主要是:

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

說白了,這所謂的基本滿足需要只是皮毛,因?yàn)樽罱诖蚶?a title="我的個(gè)人博客站點(diǎn)" target="_blank" rel="tag">我的個(gè)人博客站點(diǎn),有感于網(wǎng)頁數(shù)據(jù)庫技術(shù)的博大精深、美妙直觀,如果可以把日志用網(wǎng)頁數(shù)據(jù)庫作為讀寫的載體,豈不甚妙?

隱約中感覺這種想法似曾相識(shí),不識(shí)字只好亂翻書,果然在中發(fā)現(xiàn)有這樣一篇文章:一個(gè)基于HTML的日志和調(diào)試系統(tǒng)。有興趣的同學(xué)自己翻書吧:)。

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

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

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

項(xiàng)目中使用了消息通信機(jī)制,因?yàn)橄㈩愋头浅6啵鄳?yīng)的,處理消息的地方代碼也非常多。

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

創(chuàng)建一個(gè)缺省MFC框架程序的解決方案Test,在Test.h中看到以下內(nèi)容:

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

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

// 實(shí)現(xiàn)
    afx_msg void OnAppAbout();
    DECLARE_MESSAGE_MAP()
};

 

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

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不再解析下去,我們認(rèn)為這是一個(gè)指向特定消息對(duì)應(yīng)的實(shí)現(xiàn)函數(shù)的函數(shù)指針,這幾個(gè)宏的作用可簡(jiǎn)單理解成為Test這個(gè)項(xiàng)目定義了一個(gè)靜態(tài)的消息映射表。當(dāng)消息到來時(shí),從消息隊(duì)列中彈出消息并分發(fā)到具有入口實(shí)現(xiàn)的上層CWnd派生窗口。用戶只需要注冊(cè)消息,實(shí)現(xiàn)消息入口函數(shù)就夠了,這在MFC中一般放在.cpp文件頭部。Test.cpp中頭部有以下內(nèi)容:

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

這里是為消息枚舉值與消息實(shí)現(xiàn)函數(shù)建立映射,其中涉及到的宏的展開如下:

#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*);        // 消息執(zhí)行函數(shù)類型

struct tagMsgEntry                      // 消息入口結(jié)構(gòu)
{
    int            nMsgType;            // 消息類型
    funCall        MsgRun;              // 消息執(zhí)行函數(shù)
};

struct tagMsgMap                        // 消息映射表結(jié)構(gòu)
{
    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);
}

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

___________________________________________________________

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

// 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)                // 消息公用執(zhí)行函數(shù)
    {
        MSG_ITR itr = s_MsgMap.find(nMsgType);
        if( s_MsgMap.end() != itr )
        {
            itr->second(this);
        }
    }

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

// UserTest.cpp -- 用戶在使用時(shí)對(duì)自己關(guān)心的消息予以注冊(cè), 入口函數(shù)予以實(shí)現(xiàn)即可
REG_MSG_FUNC(MSG_SOME_ONE, SomeOneFunc)

void SomeOneFunc(CBaseMessage *pMsg)
{
    return;
}

___________________________________________________________

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

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

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

今天在查一些資料的時(shí)候,看到關(guān)于這幾個(gè)詞的論辯竟不是一般的多,細(xì)細(xì)想來,這個(gè)問題似乎也確實(shí)有解釋的必要,不在于爭(zhēng)論對(duì)錯(cuò),而在于辨明是非。

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

  • 非常大:

Douglas C. Schmidt在《C++網(wǎng)絡(luò)編程》中這樣說到:

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.

翻譯如下:

:例如,操作被異步調(diào)用時(shí),控制權(quán)交給客戶端,I/O操作請(qǐng)求則交由操作系統(tǒng)內(nèi)核處理,當(dāng)操作完成后,通過某種機(jī)制將結(jié)果通知客戶端。

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

從這兩段“very different”的解釋來看,我的感覺是并沒有指出二者的區(qū)別,因?yàn)槲覀儫o法確定所謂AIO是如何處理的,如果AIO直接“調(diào)用返回-1并置錯(cuò)誤值為EWOULDBLOCK”,實(shí)現(xiàn)“控制權(quán)交給客戶端”,似乎并無任何不妥。況且,對(duì)于非阻塞I/O,我們也需要“當(dāng)操作完成后,通過某種機(jī)制將結(jié)果通知客戶端”這樣的處理。

  • 無差別:

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

當(dāng)然,對(duì)于recv和send,我們一般會(huì)說他們是阻塞起的,而不會(huì)說他們是同步起的,但這顯然不是二者的區(qū)別,因?yàn)槲覀兌贾溃?strong>阻塞的原因正是等待同步結(jié)果的返回。

因此,二者的區(qū)別在于,阻塞/非阻塞是表現(xiàn),同步/異步是原因,我們說某某操作是阻塞起的,或者某某線程是阻塞起的,是因?yàn)樵诘却僮鹘Y(jié)果的同步返回;我們說某某操作是非阻塞的,是因?yàn)椴僮鹘Y(jié)果會(huì)通過異步方式返回。

討論到這兒,再咬文嚼字的爭(zhēng)辯下去似乎已經(jīng)沒有任何實(shí)際意義。

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

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

posted @ 2008-09-11 01:11 Fox 閱讀(4994) | 評(píng)論 (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...

____________________________

但是,我一定會(huì)看,從電視上。

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

網(wǎng)上猜測(cè)李寧點(diǎn)火的人氣很高,之前官方也有透露說會(huì)有5.12汶川大地震相關(guān)內(nèi)容,我猜想是這樣的:

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

感覺很靠譜:D。

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

0. Introduction

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

最早接觸設(shè)計(jì)模式的時(shí)候,難免被各種模式的聯(lián)系和區(qū)別所困擾,從教科書的分析可以得到模式之間形式上的不同。但這樣對(duì)于領(lǐng)會(huì)設(shè)計(jì)模式意義不大,因?yàn)槲覀冋莆漳J降哪康氖菫榱巳跁?huì)貫通,靈活運(yùn)用,以對(duì)開發(fā)有所幫助。

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

關(guān)于實(shí)現(xiàn),我不會(huì)為了厘清模式間的區(qū)別而刻意使用相似代碼實(shí)現(xiàn),相反,我會(huì)根據(jù)模式本身的適用情況舉例,而且大量代碼基于SourceMaking

_______________________________

1. Creational Design Patterns(DP)

創(chuàng)建型DP抽象了類和對(duì)象的創(chuàng)建過程,GoF給出了5種創(chuàng)建型DPAbstract FactoryBuilderFactory MethodBuilderPrototypeSingleton

2. Abstract Factory

意圖:提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無需指定它們具體的類。

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

2) 接口的參數(shù)是一個(gè)工廠對(duì)象AbstractFactory &factory)的引用,參數(shù)類型(AbstractFactory)為抽象基類,調(diào)用時(shí)根據(jù)需要傳入具體工廠對(duì)象即可;

3) 接口內(nèi)部實(shí)現(xiàn)了一系列相關(guān)或相互依賴對(duì)象(抽象產(chǎn)品)的創(chuàng)建:當(dāng)傳入具體工廠時(shí),接口實(shí)現(xiàn)的就是一系列具體產(chǎn)品的創(chuàng)建;

4) 創(chuàng)建的產(chǎn)品立即返回CreateProduct)。

參與者:

• AbstractFactory
— 聲明一個(gè)創(chuàng)建抽象產(chǎn)品對(duì)象的操作接口。

• ConcreteFactory
— 實(shí)現(xiàn)創(chuàng)建具體產(chǎn)品對(duì)象的操作。

• AbstractProduct
— 為一類產(chǎn)品對(duì)象聲明一個(gè)接口。

• ConcreteProduct
— 定義一個(gè)將被相應(yīng)的具體工廠創(chuàng)建的產(chǎn)品對(duì)象。
— 實(shí)現(xiàn)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

意圖:將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。

1) director提供抽象產(chǎn)品創(chuàng)建接口:如void Director::Construct();

2) 不同產(chǎn)品使用同一創(chuàng)建過程,由director指定特定builder以生產(chǎn)不同產(chǎn)品;

3) 接口內(nèi)部實(shí)現(xiàn)了一個(gè)復(fù)雜對(duì)象(抽象產(chǎn)品)的創(chuàng)建:當(dāng)傳入具體工廠時(shí),接口實(shí)現(xiàn)的是一個(gè)復(fù)雜的具體產(chǎn)品的創(chuàng)建;

4) 創(chuàng)建的產(chǎn)品并不立即返回創(chuàng)建完畢后返回,或使用接口GetProduct)提取結(jié)果。

參與者:

• Builder
— 為創(chuàng)建一個(gè)Product對(duì)象的各個(gè)部件指定抽象接口。

• ConcreteBuilder
— 實(shí)現(xiàn)Builder的接口以構(gòu)造和裝配該產(chǎn)品的各個(gè)部件。
— 定義并明確它所創(chuàng)建的表示。
— 提供一個(gè)檢索產(chǎn)品的接口。

• Director
— 構(gòu)造一個(gè)使用Builder接口的對(duì)象。

• Product
— 表示被構(gòu)造的復(fù)雜對(duì)象。ConcreteBuilder創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程。
— 包含定義組成部件的類,包括將這些部件裝配成最終產(chǎn)品的接口。

代碼:

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

意圖:定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。Factory Method使一個(gè)類的實(shí)例化延遲到其子類。

1) 看得出該模式其實(shí)就是C++的多態(tài)特性,借繼承實(shí)現(xiàn)。因此,其別名為虛構(gòu)造器( Virtual Constructor)

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

參與者:

• Product
— 定義工廠方法所創(chuàng)建的對(duì)象的接口。

• ConcreteProduct
— 實(shí)現(xiàn)Product接口。

• Creator
— 聲明工廠方法,該方法返回一個(gè)Product類型的對(duì)象。Creator也可以定義一個(gè)工廠方法的缺省實(shí)現(xiàn),它返回一個(gè)缺省的ConcreteProduct對(duì)象。
— 可以調(diào)用工廠方法以創(chuàng)建一個(gè)Product對(duì)象。

• ConcreteCreator
— 重定義工廠方法以返回一個(gè)ConcreteProduct實(shí)例。

代碼:

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

意圖:用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這些原型創(chuàng)建新的對(duì)象。

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

2) 根通拷貝原型實(shí)例創(chuàng)建新對(duì)象。

參與者:

• ProtoType
— 聲明一個(gè)克隆自身的接口。

• ConcreteProtoType
— 實(shí)現(xiàn)一個(gè)克隆自身的操作。

• Client
— 讓一個(gè)原型克隆自身從而創(chuàng)建一個(gè)新的對(duì)象。

代碼:

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

意圖:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。

1) 用靜態(tài)成員函數(shù)保證上述意圖。

參與者:

• Singleton
— 定義一個(gè)Instance操作,允許客戶訪問它的唯一實(shí)例。Instance是一個(gè)類操作(即C++中的一個(gè)靜態(tài)成員函數(shù))。
— 可能負(fù)責(zé)創(chuàng)建它自己的唯一實(shí)例。

 

代碼:

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

______________________________________________

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

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

一、Big-endian & Little-endian

還是Wikipedia好啊!可惜中文的國內(nèi)看不了,愚昧啊!實(shí)在覺得中文有點(diǎn)難懂,看看日本語版本吧:D!

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

關(guān)于網(wǎng)絡(luò)字節(jié)序(network byte order)主機(jī)字節(jié)序(host byte order),說來挺無關(guān)緊要的一點(diǎn)東西,因?yàn)槊看慰偸峭簦悦看味家闷娴目纯?strong>大端(big-endian)和小端(little-endian)

給定unsigned long型整數(shù)十六進(jìn)制形式: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存儲(chǔ)會(huì)使用little-endian,起初我想對(duì)于位運(yùn)算,尤其是位移運(yùn)算,little-endian很方便,但轉(zhuǎn)念一想,big-endian也方便啊,無非是左移和右移的區(qū)別而已,但little-endian的優(yōu)勢(shì)在于unsigned char/short/int/long類型轉(zhuǎn)換時(shí),存儲(chǔ)位置無需改變。

在網(wǎng)絡(luò)傳輸中,采用big-endian序,對(duì)于0x0A0B0C0D,傳輸順序就是0A 0B 0C 0D,因此big-endian作為network byte order,little-endian作為host byte order。

________________________________________________

PS:做雞有什么不好?

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

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

(滿臉疑惑)Fox:沒有。

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

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

Y:做技術(shù)沒有不好啊……

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

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

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

Y:你總要飛起來。

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

Y:……

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

Y:……

F:做雞有什么不好?

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

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

Y:……

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

原文地址:

  • 規(guī)則之例外

前面說明的編碼習(xí)慣基本是強(qiáng)制性的,但所有優(yōu)秀的規(guī)則都允許例外。

1. 現(xiàn)有不統(tǒng)一代碼(Existing Non-conformant Code)

對(duì)于現(xiàn)有不符合既定編程風(fēng)格的代碼可以網(wǎng)開一面。

當(dāng)你修改使用其他風(fēng)格的代碼時(shí),為了與代碼原有風(fēng)格保持一致可以不使用本指南約定。如果不放心可以與代碼原作者或現(xiàn)在的負(fù)責(zé)人員商討,記住,一致性包括原有的一致性。

1. Windows代碼(Windows Code)

Windows程序員有自己的編碼習(xí)慣,主要源于Windows的一些頭文件和其他Microsoft代碼。我們希望任何人都可以順利讀懂你的代碼,所以針對(duì)所有平臺(tái)的C++編碼給出一個(gè)單獨(dú)的指導(dǎo)方案。

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

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

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

3) 使用Microsoft Visual C++進(jìn)行編譯時(shí),將警告級(jí)別設(shè)置為3或更高,并將所有warnings當(dāng)作errors處理

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

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

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

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

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

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

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

  • 團(tuán)隊(duì)合作

參考常識(shí),保持一致

編輯代碼時(shí),花點(diǎn)時(shí)間看看項(xiàng)目中的其他代碼并確定其風(fēng)格,如果其他代碼if語句中使用空格,那么你也要使用。如果其中的注釋用星號(hào)(*)圍成一個(gè)盒子狀,你也這樣做:

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

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

好了,關(guān)于編碼風(fēng)格寫的差不多了,代碼本身才是更有趣的,盡情享受吧!

Benjy Weinberger
Craig Silverstein
Gregory Eitzmann
Mark Mentovai
Tashana Landray

______________________________________

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

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

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

原文地址:

  • 格式

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

1. 行長(zhǎng)度(Line Length)

每一行代碼字符數(shù)不超過80。

我們也認(rèn)識(shí)到這條規(guī)則是存有爭(zhēng)議的,但如此多的代碼都遵照這一規(guī)則,我們感覺一致性更重要。

優(yōu)點(diǎn):提倡該原則的人認(rèn)為強(qiáng)迫他們調(diào)整編輯器窗口大小很野蠻。很多人同時(shí)并排開幾個(gè)窗口,根本沒有多余空間拓寬某個(gè)窗口,人們將窗口最大尺寸加以限定,一致使用80列寬,為什么要改變呢?

缺點(diǎn):反對(duì)該原則的人則認(rèn)為更寬的代碼行更易閱讀,80列的限制是上個(gè)世紀(jì)60年代的大型機(jī)的古板缺陷;現(xiàn)代設(shè)備具有更寬的顯示屏,很輕松的可以顯示更多代碼。

結(jié)論:80個(gè)字符是最大值。例外:

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

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

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

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

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

哪怕是英文,也不應(yīng)將用戶界面的文本硬編碼到源代碼中,因此非ASCII字符要少用。特殊情況下可以適當(dāng)包含此類字符,如,代碼分析外部數(shù)據(jù)文件時(shí),可以適當(dāng)硬編碼數(shù)據(jù)文件中作為分隔符的非ASCII字符串;更常用的是(不需要本地化的)單元測(cè)試代碼可能包含非ASCII字符串。此類情況下,應(yīng)使用UTF-8格式,因?yàn)楹芏喙ぞ叨伎梢岳斫夂吞幚砥渚幋a,十六進(jìn)制編碼也可以,尤其是在增強(qiáng)可讀性的情況下——如"\xEF\xBB\xBF"是Unicode的zero-width no-break space字符,以UTF-8格式包含在源文件中是不可見的。

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

只使用空格,每次縮進(jìn)2個(gè)空格。

使用空格進(jìn)行縮進(jìn),不要在代碼中使用tabs,設(shè)定編輯器將tab轉(zhuǎn)為空格。

譯者注:在前段時(shí)間的關(guān)于Debian開發(fā)學(xué)習(xí)日記一文中,曾給出針對(duì)C/C++編碼使用的vim配置。

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

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

函數(shù)看上去像這樣:

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

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

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

甚至連第一個(gè)參數(shù)都放不下:

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

注意以下幾點(diǎn):

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

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

3) 函數(shù)名和左圓括號(hào)間沒有空格;

4) 圓括號(hào)與參數(shù)間沒有空格;

5) 左大括號(hào)(open curly brace)總在最后一個(gè)參數(shù)同一行的末尾處;

6) 右大括號(hào)(close curly brace)總是單獨(dú)位于函數(shù)最后一行;

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

8) 函數(shù)聲明和實(shí)現(xiàn)處的所有形參名稱必須保持一致;

9) 所有形參應(yīng)盡可能對(duì)齊;

10) 缺省縮進(jìn)為2個(gè)空格;

11) 獨(dú)立封裝的參數(shù)保持4個(gè)空格的縮進(jìn)。

如果函數(shù)為const的,關(guān)鍵字const應(yīng)與最后一個(gè)參數(shù)位于同一行。

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

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

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

譯者注:關(guān)于UNIX/Linux風(fēng)格為什么要把左大括號(hào)置于行尾(.cc文件的函數(shù)實(shí)現(xiàn)處,左大括號(hào)位于行首),我的理解是代碼看上去比較簡(jiǎn)約,想想行首除了函數(shù)體被一對(duì)大括號(hào)封在一起之外,只有右大括號(hào)的代碼看上去確實(shí)也舒服;Windows風(fēng)格將左大括號(hào)置于行首的優(yōu)點(diǎn)是匹配情況一目了然。

5. 函數(shù)調(diào)用(Function Calls)

盡量放在同一行,否則,將實(shí)參封裝在圓括號(hào)中。

函數(shù)調(diào)用遵循如下形式:

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

如果同一行放不下,可斷為多行,后面每一行都和第一個(gè)實(shí)參對(duì)齊,左圓括號(hào)后和右圓括號(hào)前不要留空格:

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

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

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

如果函數(shù)名太長(zhǎng),以至于超過行最大長(zhǎng)度,可以將所有參數(shù)獨(dú)立成行:

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

6. 條件語句(Conditionals)

更提倡不在圓括號(hào)中添加空格,關(guān)鍵字else另起一行。

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

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

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

如果你傾向于在圓括號(hào)內(nèi)部加空格:

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

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

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

有些條件語句寫在同一行以增強(qiáng)可讀性,只有當(dāng)語句簡(jiǎn)單并且沒有使用else子句時(shí)使用:

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

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

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

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

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

// 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. 循環(huán)和開關(guān)選擇語句(Loops and Switch Statements)

switch語句可以使用大括號(hào)分塊;空循環(huán)體應(yīng)使用{}continue

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

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

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

空循環(huán)體應(yīng)使用{}continue,而不是一個(gè)簡(jiǎn)單的分號(hào):

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. 指針和引用表達(dá)式(Pointers and Reference Expressions)

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

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

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

注意:

1) 在訪問成員時(shí),句點(diǎn)或箭頭前后沒有空格;

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

在聲明指針變量或參數(shù)時(shí),星號(hào)與類型或變量名緊挨都可以:

// 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 &

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

譯者注:個(gè)人比較習(xí)慣與變量緊挨的方式

9. 布爾表達(dá)式(Boolean Expressions)

如果一個(gè)布爾表達(dá)式超過標(biāo)準(zhǔn)行寬(80字符),如果斷行要統(tǒng)一一下。

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

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

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

譯者注:個(gè)人比較習(xí)慣邏輯運(yùn)算符位于行首,邏輯關(guān)系一目了然,各人喜好而已,至于加不加圓括號(hào)的問題,如果你對(duì)優(yōu)先級(jí)了然于胸的話可以不加,但可讀性總是差了些

10. 函數(shù)返回值(Return Values)

return表達(dá)式中不要使用圓括號(hào)。

函數(shù)返回時(shí)不要使用圓括號(hào):

return x;  // not return(x);

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

選擇=還是()

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

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

12. 預(yù)處理指令(Preprocessor Directives)

預(yù)處理指令不要縮進(jìn),從行首開始。

即使預(yù)處理指令位于縮進(jìn)代碼塊中,指令也應(yīng)從行首開始。

// 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:,每次縮進(jìn)1個(gè)空格(譯者注,為什么不是兩個(gè)呢?也有人提倡private在前,對(duì)于聲明了哪些數(shù)據(jù)成員一目了然,還有人提倡依邏輯關(guān)系將變量與操作放在一起,都有道理:-)

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

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) 所以基類名應(yīng)在80列限制下盡量與子類名放在同一行;

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

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

4) 這些關(guān)鍵詞后不要空行;

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

6) 關(guān)于聲明次序參考第三篇聲明次序一節(jié)。

14. 初始化列表(Initializer Lists)

構(gòu)造函數(shù)初始化列表放在同一行或按四格縮進(jìn)并排幾行。

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

// 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)

命名空間內(nèi)容不縮進(jìn)。

命名空間不添加額外縮進(jìn)層次,例如:

namespace {

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

}  // namespace

不要縮進(jìn):

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

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

循環(huán)和條件語句

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.

模板和轉(zhuǎn)換

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)

垂直留白越少越好。

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

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

函數(shù)頭、尾不要有空行:

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
}

______________________________________

譯者:首先說明,對(duì)于代碼格式,因人、因系統(tǒng)各有優(yōu)缺點(diǎn),但同一個(gè)項(xiàng)目中遵循同一標(biāo)準(zhǔn)還是有必要的:

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

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

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

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

5. 除函數(shù)定義的左大括號(hào)可以置于行首外,包括函數(shù)/類/結(jié)構(gòu)體/枚舉聲明、各種語句的左大括號(hào)置于行尾,所有右大括號(hào)獨(dú)立成行;

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

7. 預(yù)處理指令/命名空間不使用額外縮進(jìn),類/結(jié)構(gòu)體/枚舉/函數(shù)/語句使用縮進(jìn);

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

9. return不要加();

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

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

僅列出標(biāo)題
共7頁: 1 2 3 4 5 6 7 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲欧美日韩中文在线制服| 久久久水蜜桃| 国内精品写真在线观看| 久久久久9999亚洲精品| 亚洲狠狠婷婷| 久久久福利视频| 日韩亚洲欧美精品| 亚洲人永久免费| 最新中文字幕一区二区三区| 欧美日韩一区免费| 欧美经典一区二区| 欧美成人免费在线视频| 欧美激情中文字幕乱码免费| 久久久久女教师免费一区| 一本久久综合亚洲鲁鲁| 亚洲国产成人在线| 久久久国产视频91| 亚洲制服丝袜在线| 久久亚洲一区二区三区四区| 影音先锋一区| 亚洲日本欧美在线| 亚洲风情在线资源站| 国产午夜精品视频| 欧美午夜在线| 国产精品久久久久久久久久妞妞| 国产区精品在线观看| 国产精品日韩欧美综合 | 91久久国产自产拍夜夜嗨| 欧美精品久久久久久| 欧美成人精品激情在线观看| 可以看av的网站久久看| 欧美在线欧美在线| 亚洲激情网站| 亚洲欧美文学| 欧美另类高清视频在线| 国产拍揄自揄精品视频麻豆| 国产精品高清免费在线观看| 国产一区二区三区无遮挡| 国产亚洲欧美一区在线观看| 亚洲大片免费看| 欧美日韩中文字幕精品| 亚洲欧洲精品一区二区三区| 欧美一乱一性一交一视频| 亚洲人成网站999久久久综合| 亚洲欧美日韩国产一区二区| 校园春色综合网| 亚洲观看高清完整版在线观看| 日韩一级不卡| 国产麻豆精品视频| 麻豆视频一区二区| 亚洲欧美99| 国产日韩欧美中文| 久久久久久久久蜜桃| 亚洲欧美中日韩| 欧美日韩国产三级| 亚洲精品国产视频| 亚洲国产精品尤物yw在线观看 | 久久成人羞羞网站| 国产欧亚日韩视频| 亚洲一二三区在线| 国产精品网站在线| 欧美日韩高清在线| 久久国产视频网| 免费一级欧美在线大片| 一本一道久久综合狠狠老精东影业 | 久久综合狠狠综合久久综青草| 午夜在线精品偷拍| 亚洲区中文字幕| 国产日本欧洲亚洲| 你懂的亚洲视频| 国产精品亚洲成人| 欧美电影免费观看大全| 欧美成人亚洲成人日韩成人| 亚洲国产精品免费| 在线亚洲观看| 99国产精品自拍| 亚洲级视频在线观看免费1级| 国产精品日韩欧美综合| 欧美高清视频在线播放| 国产精品一区二区在线观看不卡| 久久久精品一区| 欧美午夜精品久久久久久超碰| 久久se精品一区二区| 国产色综合网| 久久字幕精品一区| 欧美国产一区二区三区激情无套| 久久精品91| 永久91嫩草亚洲精品人人| 欧美va日韩va| 欧美日韩国产一区二区| 国产情人综合久久777777| 久久精品国产久精国产思思| 久久婷婷久久| 久久精品亚洲国产奇米99| 欧美高清在线一区二区| 久久久精品999| 国产三级欧美三级| 欧美激情中文字幕在线| 亚洲欧美日韩国产一区二区三区 | 欧美香蕉视频| 午夜精品福利视频| 欧美一区二区网站| 一区二区日本视频| 久久精品主播| 久久久国产精品一区二区三区| 欧美午夜一区| 亚洲成人自拍视频| 欧美午夜免费电影| 欧美高清视频一区二区| 国产精品狼人久久影院观看方式| 欧美a级理论片| 国产精品午夜视频| 亚洲狼人综合| 日韩亚洲欧美成人| 久久久久五月天| 国产精品亚发布| 亚洲三级影片| 亚洲精品免费一区二区三区| 亚洲欧美日韩一区二区三区在线观看| 亚洲精品在线看| 久久久久久一区| 久久精品国产99精品国产亚洲性色| 欧美日韩国产在线播放| 欧美成人a视频| 激情综合网激情| 欧美一区二区女人| 久久精品国产清高在天天线| 国产精品久久久久久久9999 | 久久久久久久999精品视频| 欧美日韩精品一区二区| 亚洲精品永久免费| 99国产精品国产精品久久 | 国产日本欧美在线观看| 正在播放日韩| 亚洲伊人观看| 国产精品私人影院| 亚洲欧美国产日韩天堂区| 亚洲一区在线观看视频| 国产精品久久久久久久久动漫| 亚洲精品一区二区三区婷婷月 | 欧美网站在线观看| 91久久线看在观草草青青| 亚洲激情成人网| 久久一区二区三区四区| 免费在线播放第一区高清av| 1024成人| 欧美国产成人精品| 日韩午夜激情| 午夜久久久久久久久久一区二区| 国产精品久久一级| 亚洲欧美在线另类| 久久亚洲美女| 亚洲精品乱码久久久久久日本蜜臀 | 亚洲一区影院| 国产精品一二一区| 久久国产66| 亚洲国产日韩美| 亚洲欧美日本另类| 国语自产偷拍精品视频偷| 免费观看一区| 一区二区三区欧美视频| 久久久久久久久蜜桃| 亚洲精品视频免费在线观看| 欧美日韩午夜剧场| 欧美中文字幕在线| 亚洲精品一区二区三区蜜桃久| 亚洲欧美日本国产有色| 亚洲电影免费| 国产精品免费久久久久久| 久久视频一区| 亚洲一区二区欧美| 欧美成人精精品一区二区频| 一区二区三区欧美在线观看| 国产视频久久久久久久| 欧美经典一区二区| 久久国产精品毛片| 在线视频精品一| 欧美成人精品激情在线观看| 亚洲综合日韩| 日韩图片一区| 尤物九九久久国产精品的分类| 欧美午夜宅男影院在线观看| 久久久精品国产一区二区三区| 日韩视频在线一区二区| 免费不卡在线视频| 久久大综合网| 亚洲永久免费| 一本色道久久88精品综合| 好吊妞**欧美| 国产欧美在线视频| 欧美性猛交一区二区三区精品| 久久人人精品| 久久国产精彩视频| 午夜视频精品| 亚洲一线二线三线久久久| 日韩一区二区精品视频| 亚洲国产经典视频| 蜜臀av性久久久久蜜臀aⅴ四虎| 欧美一区二区三区喷汁尤物| 99这里只有久久精品视频|