• <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>
            posts - 1,  comments - 6,  trackbacks - 0

            ??? 在 Java C #等語言或者說其類庫中,都實現(xiàn)了事件模型。而 c++ 語言本身并沒有定義事件機(jī)制,并且在目前眾多優(yōu)秀的 c++ 類庫,包括 STL 、 Boost 等都沒有實現(xiàn)類似的事件機(jī)制。當(dāng)我們被 MFC 的消息搞得頭昏眼花之時,是否有沖動自己去實現(xiàn)一個簡單的事件模型呢。我想,有著相同想法的人肯定很多,而真正動手來寫可能會碰到各種各樣的困難。下面就讓我們一步步來編寫一個簡單的事件模型。

            ?

            一、 了解事件模型的機(jī)制

            在開始之前,我們有必要簡單的了解一下事件模型的機(jī)制。事實上,事件模型的機(jī)制不止一種, Java c# 的事件機(jī)制就不太一樣,不過事件模型的基礎(chǔ)都是一樣,那就是一般都使用 Observer 設(shè)計模式。關(guān)于 Observer 設(shè)計模式,希望詳細(xì)了解的朋友可以參考《設(shè)計模式》一書,在這里我們就不詳細(xì)的介紹,只是參照 c# 的事件機(jī)制來實現(xiàn)。

            c# 中,我們可以以 event 關(guān)鍵字聲明一個事件,然后我們可以將一個函數(shù)委托掛接在這個事件上,當(dāng)事件被調(diào)用時,則所有掛接的函數(shù)也會被調(diào)用。這里有一個比較難以理解的詞大概是委托,其實也不難理解。委托其實就是一種函數(shù)包裝類,這種類可以把任何函數(shù)的指針保存起來,等到需要調(diào)用的時候再調(diào)用,并且這種類的實例必須能夠掛接到事件之中。

            看了上面這段話,是不是覺得其實事件模型也并不復(fù)雜。還想對事件模型了解得更多?我這里就不繼續(xù)了,感興趣的朋友可以查找一下相關(guān)的資料。下面我們就開始在 c++ 中編寫一個簡單的事件模型。

            ?

            二、 設(shè)計一個簡單的 c++ 事件模型

            因為一個事件模型其實就是一個典型的 Observer 設(shè)計模式,因此最重要的就是 Subject( 目標(biāo)類 ) Observer( 觀察者類 ) 的設(shè)計。

            首先,我們需要一個事件類,它其實是一個抽象的 Subject 。它是一個基類,所有的其他事件都從它繼承,并且用戶只需要從這個基類繼承就可以自定義事件。我們不仿將這個類定義為 CEvent CEvent 的聲明如下:

            class CEvent ?

            {

            public :

            ????????

            ???????? typedef list < CEventHandler > data_type ;

            ?

            ???????? CEvent ();

            ?

            ???????? virtual ~ CEvent ();

            ?

            ???????? void operator ()()

            ???????? {

            ???????? ???????? data_type :: iterator it ;

            ??????????????????????????? ????????

            ?????????????????? for ( it = m_observer . begin (); it != m_observer . end (); ++ it )

            ?????????????????? {

            ?????????????????? ???????? (* it )(* this );

            ?????????????????? }

            ???????? }

            ?

            ???????? CEvent & operator += ( const CEventHandler & handler )

            ???????? {

            ???????? ???????? Register ( handler );

            ?????????????????? return * this ;

            ???????? }

            ?

            ???????? CEvent & operator -= ( const CEventHandler & handler )

            ???????? {

            ???????? ???????? UnRegister ( handler );

            ?????????????????? return * this ;

            ???????? }

            ?

            ???????? void Register ( const CEventHandler & handler )

            ???????? {

            ???????? ???????? m_observer . push_back ( handler );

            ???????? }

            ?

            ???????? void UnRegister ( const CEventHandler & handler )

            ???????? {

            ???????? ???????? m_observer . remove ( handler );

            ???????? }

            ?

            protected :

            ?

            ???????? // 將偵聽的所有函數(shù)或者仿函數(shù)的集合起來

            ???????? data_type m_observer ;

            };

            ?

            其次,這個事件可以掛接任意的函數(shù),也就是它可以保存所有的掛接函數(shù)。在 c++ 中,我們大致有三種類型的函數(shù),全局或者靜態(tài)函數(shù)、成員函數(shù)、仿函數(shù)。要在 CEvent 中實現(xiàn)一個或者多個方法來執(zhí)行掛接的任務(wù)不太現(xiàn)實。我們參考 c# 的實現(xiàn)方法,可以先實現(xiàn)一個委托類,將各種函數(shù)保存起來。然后再使用 CEvent 對這個委托類進(jìn)行掛接。我們不仿稱這個類為 CEventHandler 。

            然后,我們的這個委托類需要能夠保存所有的函數(shù)。在 c++ 里,我們能夠很容易的得到函數(shù)的指針,只需要在函數(shù)名前加 & ,但是各種函數(shù),特別是成員函數(shù)它是沒有類型的,這就要有一種機(jī)制,將所有的函數(shù)轉(zhuǎn)換為同一種類型。因為函數(shù)是沒有類型,也相當(dāng)于每一個函數(shù)都是一種類型,解決這個問題的方法其實就是使用模板類。因此,我們先定義一個虛基類 CFunImpl 。對三種不同的函數(shù),我們分別從 CfunImpl 繼承三種不同的子類, CstaticFun 、 CmemFun,CFunctor 。 CstaticFun 表示全局的或者靜態(tài)的函數(shù),不過這必須是一個模板類,對于每一個靜態(tài)或者全局函數(shù),都是模板類的一個特化。類聲明的代碼如下:

            template < typename Fun >

            ???????? class CStaticFun : public CFunImpl

            ???????? {

            ???????? public :

            ?

            ???????? ???????? CStaticFun ( const Fun & fun ) : m_fun ( fun ){};

            ??????????????????

            ?????????????????? virtual ~ CStaticFun ()

            ?????????????????? {

            ?????????????????? }

            ???????? protected :

            ?????????????????? Fun m_fun ;

            ???????? };

            ?

            對于 CmemFun 類,我們?nèi)绶ㄅ谥?,不過我們這時需要保存兩個成員變量,一個是成員函數(shù)的指針,另一個則是該成員函數(shù)所屬的對象的指針。類聲明的代碼如下:

            ?

            template < typename PointerToObj , typename PointerToMemFun >

            ???????? class CMemFun : public CFunImpl

            ???????? {

            ???????? public :

            ?

            ???????? ???????? CMemFun ( const PointerToObj & pObj , PointerToMemFun pMemFn )

            ??????????????????????????? : m_pObj ( pObj ), m_pMemFun ( pMemFn )

            ?????????????????? {

            ?????????????????? }

            ?

            virtual ~ CMemFun ()

            ?????????????????? {

            ?????????????????? }

            ??????????????????

            ???????? protected :

            ???????? ???????? PointerToObj m_pObj ;

            ???????? ???????? PointerToMemFun m_pMemFun ;

            };

            ?

            對于仿函數(shù),我們這里先不講,因為仿函數(shù)使用得很少,并且這還涉及到仿函數(shù)的概念,有興趣的朋友可以在閱讀完本文之后自己來實現(xiàn)。

            好,到現(xiàn)在為止,我們可以將所有的函數(shù)使用 CfunImpl 來表示了。因此,我們可以在委托類 CEventHandler 中保存 CfunImpl* 來達(dá)到我們的目的。 CEventHandler 的聲明如下:

            ???????? class CEventHandler ?

            ???????? {

            ???????? public :

            ?????????????????? virtual ~ CEventHandler ()

            ?????????????????? {

            ?????????????????? ???????? Clear ();

            ?????????????????? }

            ?

            ???????? ???????? template < class Fun >

            ???????? ???????? CEventHandler ( const Fun & fun )

            ??????????????????????????? : m_pImpl ( new CStaticFun < Fun >( fun ))

            ?????????????????? {}

            ??????????????????

            ???????? ???????? CEventHandler ( const CEventHandler & fun )

            ??????????????????????????? : m_pImpl ( NULL )

            ?????????????????? {

            ?????????????????? ???????? * this = fun ;

            ?????????????????? }

            ?

            ?????????????????? void Clear ()

            ?????????????????? {

            ??????????????????????????? if ( m_pImpl )

            ??????????????????????????? {

            ??????????????????????????? ???????? delete m_pImpl ;

            ??????????????????????????? ???????? m_pImpl = NULL ;

            ??????????????????????????? }

            ?????????????????? }

            ?

            ???????? ???????? CEventHandler & operator = ( const CEventHandler & fun )

            ?????????????????? {

            ?????????????????? ???????? Clear ();

            ?

            ??????????????????????????? if ( fun . m_pImpl )

            ??????????????????????????? {

            ??????????????????????????? ???????? m_pImpl = fun . m_pImpl -> Clone ();

            ??????????????????????????? }

            ?

            ?????????????????? ???????? return * this ;

            ?????????????????? }

            ??????????????????

            ?????????????????? // 成員函數(shù)

            ???????? ???????? template < typename PointerToObj , typename PointerToMemFun >

            ?????????????????? ???????? CEventHandler ( const PointerToObj & pObj , const PointerToMemFun & pMemFun )

            ??????????????????????????? : m_pImpl ( new CMemFun < PointerToObj , PointerToMemFun >( pObj , pMemFun ))

            ?????????????????? {}

            ?

            ?????????????????? void operator ()( CEvent & e )

            ?????????????????? {

            if ( m_pImpl )

            ??????????????????????????? {

            ??????????????????????????? ???????? (* m_pImpl )( e );

            }

            ?????????????????? }

            ?

            ?????????????????? bool operator == ( const CEventHandler & handler )

            ?????????????????? {

            ??????????????????????????? if ( m_pImpl == NULL || handler . m_pImpl == NULL )

            ??????????????????????????? {

            ??????????????????????????? ???????? return true ;

            ??????????????????????????? }

            ?

            ??????????????????????????? if ( typeid ( m_pImpl ) == typeid ( handler . m_pImpl ))

            ??????????????????????????? {

            ??????????????????????????? ???????? return (* m_pImpl ) == (*( handler . m_pImpl ));

            ??????????????????????????? }

            ?

            ?????????????????? ???????? return false ;

            ?????????????????? }

            ?

            ???????? protected :

            ???????? ???????? CFunImpl * m_pImpl ;

            ?

            ???????? };

            ?

            不過要實現(xiàn)事件機(jī)制,我們還需要 CfunImpl* 的子類必須實現(xiàn)幾個方法。

            第一個方法就是調(diào)用函數(shù)的方法,我們這里要求重載 void operator()(CEvent& e); 來達(dá)到目的。

            第二個方法就是 operator== ,為什么需要這個方法呢。因為對于 CEvent 來說,調(diào)用 UnRegister() 時,我們必須找到我們使用 Register() 添加到 list 中的函數(shù),這時我們需要判斷兩個 CEventHandler 對象是否相等,這是件麻煩事情。 CEventHandler 保存的是 CFunImpl 的指針,好吧,這個我們就要求 CfunImpl 的子類必須實現(xiàn)重載 operator== 。

            第三個方法,從 CfunImpl 繼承必須實現(xiàn) Clone() 方法。 Clone() 方法可以讓 CEventHandler 實現(xiàn)拷貝。具體的實現(xiàn)方法我們可以查看源碼。

            ?

            三、 使用事件模型

            一個簡單的事件模型基本完成了。現(xiàn)在我們可以編寫一個例子來演示一下如何來使用這個事件模型。其實使用起來很簡單。

            我們可以聲明一個 CEvent 或者 CEvent 子類的實例。讓需要監(jiān)聽的函數(shù)掛接到這個 CEvent 中,我們實現(xiàn)了 Register() 函數(shù)和 += 操作符,都可以使用。掛接的語句就像這樣:

            CEvent evt;

            CobserverTest obj;

            evt += CEventHandler (& obj , &( CObserverTest :: OnStartEvent ));

            當(dāng)事件的 operator() 方法調(diào)用時,就會自動調(diào)用我們的監(jiān)聽函數(shù)。具體的例子可以查看提供的源代碼。

            ?

            四、 模型的缺陷和改進(jìn)

            到現(xiàn)在為止,我們終于實現(xiàn)了一個簡單的事件模型。它可以有效的工作,并且與平臺無關(guān)。當(dāng)然,要求你的編譯器盡量的支持 c++ 標(biāo)準(zhǔn)。如果你的編譯器對 c++98 標(biāo)準(zhǔn)支持不夠,也許它不能夠工作正常。筆者在 VC6SP6 VC2005 中測試均通過。

            不過這個模型還有可以改進(jìn)的余地。其一、目前只是支持同步操作,即當(dāng)你的事件進(jìn)行調(diào)用時是在同一線程里順序進(jìn)行。要改進(jìn)到支持異步調(diào)用就必須在每次調(diào)用時在一個新的線程中進(jìn)行調(diào)用。因此我們需要一個線程機(jī)制來完成這個工作。如果在一個特定的平臺中使用,如 VC 中,我們可以在響應(yīng)函數(shù)中創(chuàng)建一個新的線程。

            其二、目前的所有的函數(shù)參數(shù)必須為 (CEvent& e) ,不能寫成 (CchildEvent& e) 。要對這個限制進(jìn)行改進(jìn),就需要編譯器支持虛模板成員函數(shù)的機(jī)制。不過在 VC6 VC2005 中,均不支持這種特性。因此要么尋求它法,要么就等到編譯器的支持吧。

            其三、線程安全,這個簡單的事件模型使用到了 STL 中的 list 組件,但是并非所有的 list 實現(xiàn)都是線程安全的。建議大家采用 stlport ,因為它的實現(xiàn)是線程安全的,如果沒有采用線程安全的實現(xiàn),那么在調(diào)用 Register UnRegister 時最好能夠?qū)崿F(xiàn)互斥。

            ?

            限于筆者知識水平和文筆有限,不當(dāng)之處肯定很多。若你有好的想法和建議,可以 email 告訴我。本文中所涉及到的所有源碼可以從這里下載http://www.shnenglu.com/Files/lunny/CEventModel.rar

            ?

            ????????????? ???????????????????? ???????????????????? ?????? ????????????? ???????????????????? ????????????? 肖倫文

            xiaolunwen@hotmail.com
            posted on 2006-07-30 20:48 風(fēng)間蒼月 閱讀(4279) 評論(6)  編輯 收藏 引用
            久久精品国产精品亚洲精品| 一本色道久久88综合日韩精品| 久久精品麻豆日日躁夜夜躁| 久久婷婷五月综合国产尤物app| 久久综合狠狠综合久久| 99久久精品国产高清一区二区 | 性高湖久久久久久久久AAAAA| 久久久久久国产精品无码下载| 欧美精品乱码99久久蜜桃| 日韩精品久久久肉伦网站| 免费国产99久久久香蕉| 久久91精品国产91| 久久精品视频网| 欧美激情一区二区久久久| 国产国产成人精品久久| 久久se精品一区二区影院| 精品久久久久久中文字幕大豆网| 狠狠色丁香久久综合五月| 性做久久久久久久久老女人| 久久综合噜噜激激的五月天| 久久精品二区| 国内精品九九久久久精品| 久久伊人中文无码| 国产成人久久精品一区二区三区| 国产视频久久| 久久精品麻豆日日躁夜夜躁| 久久亚洲AV永久无码精品| 99久久久精品免费观看国产| 无码任你躁久久久久久老妇| 国产精品久久成人影院| 久久WWW免费人成一看片| 成人精品一区二区久久| 青青草原精品99久久精品66| 亚洲午夜无码AV毛片久久| 热re99久久精品国产99热| 欧洲人妻丰满av无码久久不卡 | 国产精品免费久久| 久久久久无码精品国产| 日本WV一本一道久久香蕉| 久久一区二区三区99| 国产69精品久久久久99|