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

            Event Programming in C++ (Part I)


            Q:
            微軟的.NET框架讓我們能夠?yàn)橥泄茴?lèi)定義事件并通過(guò)代理和+=操作符對(duì)其進(jìn)行處理.那么在本地C++中有沒(méi)有同樣的方法呢,它看起來(lái)很有用.

                  某些讀者

             

            A:事實(shí)上確實(shí)有! Visual C++? .NET有種稱(chēng)為統(tǒng)一事件模型的東西能讓你用和托管類(lèi)同樣的途徑實(shí)現(xiàn)本地事件(通過(guò)__event關(guān)鍵字),但是本地事件有些微軟都沒(méi)計(jì)劃要修正的隱晦的技術(shù)問(wèn)題,因此他們讓我正式的去阻止你們用它.這是不是意味著C++程序員們只有生活在沒(méi)有事件的世界中呢?當(dāng)然不是!不止一種方法能剝貓皮(血腥!).我將告訴你們?cè)鯓佑袟l不紊的實(shí)現(xiàn)自己華麗的事件系統(tǒng).

            在此之前,我先大概說(shuō)下關(guān)于事件和事件編程的東西,這很重要!在現(xiàn)在這個(gè)年月你不能對(duì)事件的理解沒(méi)有一個(gè)堅(jiān)實(shí)的基礎(chǔ)就來(lái)編寫(xiě)代碼--它們是什么?什么時(shí)候該用它們?

            成功的設(shè)計(jì)全是針對(duì)降低復(fù)雜性的.很久以前,當(dāng)函數(shù)還被稱(chēng)作子程序”(我本人為證,我確信),降低復(fù)雜性的主要途徑就是自上而下的編程.你從一個(gè)高層次的目的入手如為宇宙建模”,然后把它分解成較小的任務(wù)如為銀河系建模為太陽(yáng)系建模”,然后... 直到這些任務(wù)變得簡(jiǎn)單到能在一個(gè)函數(shù)里實(shí)現(xiàn).自上而下的設(shè)計(jì)仍然被運(yùn)用在在程序設(shè)計(jì)中,但是當(dāng)系統(tǒng)要對(duì)發(fā)生順序不確定的實(shí)時(shí)事件作出響應(yīng)時(shí),它就不能很好的工作了.一個(gè)經(jīng)典的例子就是必須對(duì)用戶(hù)操作如點(diǎn)擊一個(gè)按鍵或移動(dòng)鼠標(biāo)作出響應(yīng)的GUI程序.事實(shí)上, 圖形用戶(hù)接口的出現(xiàn)很大程度上刺激了事件編程的發(fā)展.

            在自上而下的模式中,處在頂層的高層次模塊通過(guò)調(diào)用像DoThis,DoThat的函數(shù)來(lái)驅(qū)使低層次模塊來(lái)完成不同的任務(wù).但是低層次模塊遲早需要向上回饋.Windows,你可以讓矩形或橢圓形畫(huà)出它自己,但最終Windows需要調(diào)用你的應(yīng)用程序來(lái)顯示它的窗口.但是你的應(yīng)用程序甚至還根本不存在,它還在計(jì)劃中!那么Windows怎樣才能知道該去調(diào)用哪個(gè)函數(shù)呢?這里就是事件的用武之地.

                                                   圖自上而下 vs 自下而上

            所有基于Windows的應(yīng)用程序-不管是直接用C編寫(xiě),還是用封裝在MFC或者.NET框架中的類(lèi)-的核心就是一個(gè)處理諸如WM_PAINT,WM_SETFOCUS這樣的消息的窗口過(guò)程.(或者說(shuō)是MFC.NET)實(shí)現(xiàn)了這個(gè)窗口過(guò)程并且把它傳遞給Windows操作系統(tǒng).當(dāng)需要描繪,改變焦點(diǎn)或者激活窗口時(shí),Windows用適當(dāng)?shù)南⒋a通知你的程序.這些消息就是事件.你的窗口過(guò)程就是事件處理者.

            如果說(shuō)過(guò)程編程是自上而下,那事件編程就是自下而上.在一個(gè)典型的軟件系統(tǒng)中,函數(shù)調(diào)用是從較高層模塊流向較底層模塊;然而事件以相反的方向流動(dòng).1說(shuō)明了這個(gè)模式.當(dāng)然,我們的現(xiàn)實(shí)世界并不總是這么層次分明的.

            很多軟件系統(tǒng)看起來(lái)更像圖2所述.


                                 




                                                      圖混合模式

            那么嚴(yán)格來(lái)講到底什么是事件呢?本質(zhì)上來(lái)說(shuō)它是一個(gè)回調(diào).模塊用調(diào)用你在運(yùn)行時(shí)提供的函數(shù)這條途徑取代了了調(diào)用一個(gè)函數(shù)名稱(chēng)在編譯時(shí)究已知的函數(shù).Windows,它就叫窗口過(guò)程..NET框架中它就叫委托.不論術(shù)語(yǔ)怎么講,事件為軟件模塊提供了一條調(diào)用直到運(yùn)行時(shí)才知道的函數(shù)的途徑.回調(diào)就是事件處理程序.激發(fā)一個(gè)事件就意味著會(huì)調(diào)用事件處理程序.第一個(gè)接收者交給發(fā)送者一個(gè)注冊(cè)事件處理程序的指針.

            下面是些我們通常會(huì)用到事件的一些情形.

            向客戶(hù)端通報(bào)實(shí)時(shí)事件:用戶(hù)按下一個(gè)按鍵;時(shí)間到了午夜;風(fēng)扇停止,CPU著火了.

             

            報(bào)告一個(gè)時(shí)間很長(zhǎng)的操作的進(jìn)展情況:當(dāng)拷貝文件或者搜索一個(gè)海量數(shù)據(jù)庫(kù)時(shí),組件可能會(huì)定期的喚起一個(gè)事件來(lái)報(bào)告已經(jīng)拷貝了多少文件或者已經(jīng)搜索了多少條記錄.

             

            報(bào)告一些重要的或者感興趣的事情的發(fā)生:如果你在你的程序中用IWebBrowser2訪(fǎng)問(wèn)Microsoft Internet Explorer,在導(dǎo)航到一個(gè)新頁(yè)面之前和之后或者當(dāng)它創(chuàng)建了一個(gè)新窗口等等它都會(huì)通報(bào)你.

             

            調(diào)用庫(kù)函數(shù):C運(yùn)行時(shí)庫(kù)函數(shù) qsort可以對(duì)對(duì)象數(shù)組排序,但是你必須將要比較的對(duì)象提供給這個(gè)函數(shù).很多STL容器都有這些技巧.大多數(shù)程序員不會(huì)說(shuō)qsort回調(diào)了一個(gè)事件,但是沒(méi)理由你不能這么想.它是”time to compare”事件.

             

            一些讀者偶爾會(huì)問(wèn):異常和事件的區(qū)別是什么?最主要的區(qū)別是異常描述的是假定不會(huì)發(fā)生卻發(fā)生了的意想不到的情形.比如你的程序用光了內(nèi)存或用零做了除數(shù).,這就是你不希望發(fā)生的異常的情形,一旦它們發(fā)生了,你的程序必須對(duì)它進(jìn)行處理.然而事件是正常的日常操作的一部分并且完全是意料之中的.用戶(hù)移動(dòng)了鼠標(biāo)或按下一個(gè)按鍵.瀏覽器導(dǎo)航到新頁(yè)面.從控制流的角度來(lái)看,事件就是一個(gè)函數(shù)調(diào)用,而異常是跨越調(diào)用棧的長(zhǎng)跳轉(zhuǎn).

            對(duì)于事件最普遍的誤解是:它們是異步的.盡管事件經(jīng)常被用來(lái)處理用戶(hù)輸入及其他異步操作,事件本身是同步發(fā)生的.激活一個(gè)事件和調(diào)用事件處理是一回事.它看起來(lái)像下面這段偽碼:

            // raise Foo event

            for (/* each registered object */) {

              obj->FooHandler(/* args */);

            }

            控制權(quán)立即被交給事件處理程序,直到它處理完成才會(huì)返回.有些系統(tǒng)提供了異步方式激活事件的途徑;Windows讓你用PostMessage替代SendMessage.控制權(quán)立即從PostMessage回收,而消息稍后才會(huì)被處理.

            但是.NET框架事件和我在此討論的事件都是一旦被激活就立刻被處理.當(dāng)然,你總是能夠從運(yùn)行在單獨(dú)的線(xiàn)程的代碼里激活事件,或者用異步委托調(diào)用來(lái)執(zhí)行導(dǎo)致事件異步(相對(duì)于主線(xiàn)程)發(fā)生的線(xiàn)程池里的每一個(gè)事件處理程序.

            Windows處理事件的方式(擁有大量窗口過(guò)程和類(lèi)型一致的WPARAM/LPARAM參數(shù)),以現(xiàn)代程序設(shè)計(jì)的標(biāo)準(zhǔn)來(lái)看也是相當(dāng)簡(jiǎn)單的.因此所有的Windows程序都在用這一套機(jī)制,即使是今天.有些程序員甚至創(chuàng)建不可視窗口來(lái)傳遞事件.窗口過(guò)程并不是真實(shí)的事件機(jī)制,原因在于Windows只允許每個(gè)窗口只能有一個(gè)窗口過(guò)程,因而如果每個(gè)窗口過(guò)程都調(diào)用它之前的,則多個(gè)窗口過(guò)程就能鏈接起來(lái).這種流程就是子過(guò)程.在真實(shí)的事件系統(tǒng)中,可以不分層次的為同樣的事件注冊(cè)不只一個(gè)的接收者.

            .NET框架中,事件機(jī)制很健全.任何對(duì)象都能定義事件,并且多個(gè)對(duì)象可以監(jiān)聽(tīng)它們..NET,事件通過(guò)委托(.NET中回調(diào)的替代詞)來(lái)工作.更重要的是委托是類(lèi)型安全的,不再有void*WPARAM/LPARAM之類(lèi)的東西.

            在托管擴(kuò)展中,要定義一個(gè)事件可以用__event關(guān)鍵字.例如Windows::Forms中的Button類(lèi)有一個(gè)Click事件:

            // in Button class

            public:

              __event EventHandler* Click;

            事件處理程序是一個(gè)接受一個(gè)ObjectEventArgs參數(shù)的函數(shù)委托:

            public __delegate void EventHandler(

               Object* sender,

               EventArgs* e

            );

            要接收事件你要實(shí)現(xiàn)一個(gè)有正確的參數(shù)的處理者成員函數(shù)并創(chuàng)建一個(gè)委托封裝它,然后調(diào)用事件操作符”+=”來(lái)注冊(cè)你的處理函數(shù)/委托.對(duì)于Click事件,它看起來(lái)像這樣:

            // event handler

            void CMyForm::OnAbort(Object* sender, EventArgs *e)

            {

              ...

            }

            // register my handler

            m_abortButton->Click += new EventHandler(this, OnAbort);

            注意處理函數(shù)必須和委托定義的參數(shù)一樣.所有這些都能在MSDNManaged Extensions 101找到.但是你問(wèn)的并不是托管事件而是本地事件-怎樣在本地C++中實(shí)現(xiàn)事件?C++沒(méi)有內(nèi)建的事件機(jī)制,你能做什么呢?你能用typedef來(lái)定義一個(gè)回調(diào)函數(shù)然后讓客戶(hù)接受它,有點(diǎn)像qsort那頂舊帽子.更不畢說(shuō)當(dāng)你要處理幾個(gè)事件時(shí)會(huì)有多麻煩.當(dāng)你想用成員函數(shù)取代靜態(tài)外部函數(shù)來(lái)作為事件處理函數(shù),它會(huì)尤其丑陋!

            定義事件更好的方法是創(chuàng)建一個(gè)接口.COM就是這么做的.但是你不畢用C++實(shí)現(xiàn)所有的COM代碼,你可以用一個(gè)簡(jiǎn)單的類(lèi).我以自己寫(xiě)的一個(gè)類(lèi)名為CPrimerCalculator的類(lèi)為例,它用來(lái)找出的質(zhì)數(shù).當(dāng)它運(yùn)行時(shí),它會(huì)激活兩種類(lèi)型的事件:一個(gè)進(jìn)度事件和一個(gè)完成事件.這些事件由IPrimeEvents接口定義..NETCOM的角度來(lái)看IPrimeEvents就是一個(gè)接口;它就是一個(gè)普通而古老的定義了簽名(參數(shù)和返回類(lèi)型)C++抽象類(lèi).所有處理CPrimerCalculator事件的客戶(hù)都必須實(shí)現(xiàn)IPrimeEvents,然后調(diào)用CPrimeCalculator::Register注冊(cè)它們的接口.CPrimeCalculator將這些對(duì)象/接口添加到自身的鏈表中去.當(dāng)CPrimeCalculator測(cè)試每個(gè)整數(shù)的質(zhì)數(shù)性時(shí),它會(huì)周期性的報(bào)告截止到目前一共找到了多少質(zhì)數(shù):

            // in CPrimeCalculator::FindPrimes

            for (UINT p=2; p<max; p++) {

               // figure out if p is prime

               if (/* every now and then */)

                  NotifyProgress(GetNumberOfPrimes());

               ...

            }

            NotifyDone();

            CPrimeCalculator調(diào)用自己的助手函數(shù)NotifyProgressNotifyDone來(lái)激活事件.這些函數(shù)遍歷事件鏈表,調(diào)用每個(gè)客戶(hù)的合適的事件處理函數(shù).代碼如下:

            void CPrimeCalculator::NotifyProgress(UINT nFound)

            {

              list<IPrimeEvents*>::iterator it;

              for (it=m_clients.begin(); it!=m_clients.end(); it++) {

                (*it)->OnProgress(nFound);

              }

            }

            如果你還沒(méi)忘記STL,你會(huì)明白迭代器的解除引用操作符會(huì)返回當(dāng)前對(duì)象,上面代碼中的for循環(huán)里的那一行等同于下面:

            IPrimeEvents* obj = *it;

            obj->OnProgress(nFound);

            有一個(gè)類(lèi)似的不帶參數(shù)的NotifyDone函數(shù)能激發(fā)完成事件.你應(yīng)該明白在客戶(hù)知道當(dāng)FindPrimes返回控制權(quán)時(shí)CPrimeCalculator已經(jīng)完成之前沒(méi)有必要用完成事件.除了一種情況以外你可能是對(duì)的.那就是可能不止有一個(gè)客戶(hù)為接收事件注冊(cè)了,并且很可能不是同一個(gè)客戶(hù)調(diào)用了CPrimeCalculator::FindPrimes.3就是我的PrimeCalc測(cè)試程序.PrimeCalc為質(zhì)數(shù)事件實(shí)現(xiàn)了兩種不同的事件處理過(guò)程.第一個(gè)就是主對(duì)話(huà)框本身CMyDlg,它利用多重繼承實(shí)現(xiàn)了IPrimeEvents接口.這個(gè)對(duì)話(huà)框處理了OnProgressOnDone事件,它在窗口中顯示進(jìn)度并且當(dāng)完成是有提示音.另一個(gè)是CTracePrimeEvents,它同樣實(shí)現(xiàn)了IPrimeEvents接口.它的實(shí)現(xiàn)是顯示診斷信息.寫(xiě)CTracePrimeEvents的目的是為了展示怎樣為同樣的事件注冊(cè)一個(gè)以上的客戶(hù).

                                 

                                                圖 3 PrimeCalc in Action

               在用CPrimeCalculator寫(xiě)應(yīng)用程序的程序員看來(lái)處理事件是很簡(jiǎn)單而清晰的.IPrimeEvents派生,實(shí)現(xiàn)處理函數(shù),然后注冊(cè)自己.在通過(guò)寫(xiě)各種類(lèi)來(lái)激發(fā)事件的程序員看來(lái),這個(gè)過(guò)程是有點(diǎn)單調(diào)乏味的.首先你得定義事件接口,這還湊活.但緊接著你的寫(xiě)注冊(cè)和解除注冊(cè)函數(shù),更不畢說(shuō)為每一個(gè)Foo事件寫(xiě)下NotifyFoo函數(shù).如果你有15個(gè)事件,特別是每一個(gè)NotifyFoo函數(shù)都像一個(gè)娘胎里出來(lái)似的,這就相當(dāng)討厭了.

            void CMyClass::NotifyFoo(/* args */)

            {

              list<IPrimeEvents*>::iterator it;

              for (it=m_clients.begin(); it!=m_clients.end(); it++) {

                (*it)->OnFoo(/* args */);

              }

            }

                                    
                                             圖 4 PrimeCalcTraceWin中的輸出

               迭代客戶(hù)鏈表中的NotifyFoo,為每一個(gè)已注冊(cè)客戶(hù)調(diào)用適當(dāng)?shù)?/SPAN>OnFoo處理函數(shù),傳給它需要的任何參數(shù).有沒(méi)有什么方法能用宏或模板或其它什么東西把它通用化來(lái)減輕這份苦差事把你從寫(xiě)一些令人厭煩的樣板代碼中解放出來(lái)呢?事實(shí)上,確實(shí)有!在下個(gè)月我會(huì)向你們展示.同樣的時(shí)間,同樣的頻道.在此之前-Happy Programming!

            posted on 2006-03-16 11:08 Dr.Magic 閱讀(1177) 評(píng)論(2)  編輯 收藏 引用 所屬分類(lèi): 譯海淺涉

            FeedBack:
            # re: Event Programming in C++ (Part I)
            2006-03-19 11:26 | 編程浪子
            不錯(cuò)!很好的思路!!!!不過(guò)語(yǔ)法有些不太通順!!!哈哈!謝謝先  回復(fù)  更多評(píng)論
              
            # re: Event Programming in C++ (Part I)
            2006-04-17 14:38 | 猩猩
            圖片也顯示不出來(lái)  回復(fù)  更多評(píng)論
              

            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            品成人欧美大片久久国产欧美... 品成人欧美大片久久国产欧美 | 一本伊大人香蕉久久网手机| 国产午夜久久影院| 久久久久久久综合综合狠狠| 久久99热这里只有精品66| 九九精品99久久久香蕉| 久久精品国产99国产精品澳门| 国产精品九九久久免费视频| 精品伊人久久久| 久久亚洲欧洲国产综合| 麻豆精品久久久一区二区| 亚洲成av人片不卡无码久久 | 亚洲国产精品久久久久| 久久精品亚洲精品国产色婷| 精品久久久无码21p发布| 99久久精品日本一区二区免费| www亚洲欲色成人久久精品| 波多野结衣久久精品| 99久久精品免费观看国产| 色综合久久最新中文字幕| 久久国产成人精品麻豆| 超级碰碰碰碰97久久久久| 久久久久久久精品妇女99| 亚洲精品乱码久久久久久蜜桃| 久久se精品一区二区| 韩国三级中文字幕hd久久精品| 久久久久久国产精品无码下载| 要久久爱在线免费观看| 色99久久久久高潮综合影院| 人妻无码精品久久亚瑟影视| 人人狠狠综合久久亚洲婷婷 | 亚洲综合久久夜AV | 99久久国产热无码精品免费久久久久 | 欧洲性大片xxxxx久久久| 午夜福利91久久福利| 99国产精品久久久久久久成人热| 久久99精品久久久久久| 青青草原综合久久大伊人导航| 国产精品99久久久久久宅男小说| 91精品国产91久久| 久久久久久亚洲AV无码专区|