• <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>
            We do not always find visible happiness in proportion to visible virtue

            夢幻白樺林

            SHARE

              C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              14 Posts :: 58 Stories :: 62 Comments :: 0 Trackbacks

            公告

            常用鏈接

            留言簿(5)

            搜索

            •  

            最新隨筆

            最新評論

            閱讀排行榜

            委托(delegate)
              
              和成員函數(shù)指針不同,你不難發(fā)現(xiàn)委托的用處。最重要的,使用委托可以很容易地實現(xiàn)一個 Subject/Observer設(shè)計模式的改進版[GoF, p. 293]。Observer(觀察者)模式顯然在GUI中有很多的應(yīng)用,但我發(fā)現(xiàn)它對應(yīng)用程序核心的設(shè)計也有很大的作用。委托也可用來實現(xiàn)策略(Strategy)[GoF, p. 315]和狀態(tài)(State)[GoF, p. 305]模式。
              
              現(xiàn)在,我來說明一個事實,委托和成員函數(shù)指針相比并不僅僅是好用,而且比成員函數(shù)指針簡單得多!既然所有的.NET語言都實現(xiàn)了委托,你可能會猜想如此高層的概念在匯編代碼中并不好實現(xiàn)。但事實并不是這樣:委托的實現(xiàn)確實是一個底層的概念,而且就像普通的函數(shù)調(diào)用一樣簡單(并且很高效)。一個C++委托只需要包含一個this 指針和一個簡單的函數(shù)指針就夠了。當你建立一個委托時,你提供這個委托一個this指針,并向它指明需要調(diào)用哪一個函數(shù)。編譯器可以在建立委托時計算出調(diào)整this指針需要的偏移量。這樣在使用委托的時候,編譯器就什么事情都不用做了。這一點更好的是,編譯器可以在編譯時就可以完成全部這些工作,這樣的話,委托的處理對編譯器來說可以說是微不足道的工作了。在x86系統(tǒng)下將委托處理成的匯編代碼就應(yīng)該是這么簡單:
              
              mov ecx, [this]
              
              call [pfunc]
              
              但是,在標準C++中卻不能生成如此高效的代碼。 Borland為了解決委托的問題在它的C++編譯器中加入了一個新的關(guān)鍵字(__closure),用來通過簡潔的語法生成優(yōu)化的代碼。GNU編譯器也對語言進行了擴展,但和Borland的編譯器不兼容。如果你使用了這兩種語言擴展中的一種,你就會限制自己只使用一個廠家的編譯器。而如果你仍然遵循標準C++的規(guī)則,你仍然可以實現(xiàn)委托,但實現(xiàn)的委托就不會是那么高效了。
              
              有趣的是,在C#和其他.NET語言中,執(zhí)行一個委托的時間要比一個函數(shù)調(diào)用慢8倍(參見http://msdn.microsoft.com/library/en- us/dndotnet/html/fastmanagedcode.asp)。我猜測這可能是垃圾收集和.NET安全檢查的需要。最近,微軟將“統(tǒng)一事件模型(unified event model)”加入到Visual C++中,隨著這個模型的加入,增加了__event、 __raise、__hook、__unhook、event_source和event_receiver等一些關(guān)鍵字。坦白地說,我對加入的這些特性很反感,因為這是完全不符合標準的,這些語法是丑陋的,因為它們使這種C++不像C++,并且會生成一堆執(zhí)行效率極低的代碼。
              
              解決這個問題的推動力:對高效委托(fast delegate)的迫切需求
              
              使用標準C++實現(xiàn)委托有一個過度臃腫的癥狀。大多數(shù)的實現(xiàn)方法使用的是同一種思路。這些方法的基本觀點是將成員函數(shù)指針看成委托??但這樣的指針只能被一個單獨的類使用。為了避免這種局限,你需要間接地使用另一種思路:你可以使用模版為每一個類建立一個“成員函數(shù)調(diào)用器(member function invoker)”。委托包含了this指針和一個指向調(diào)用器(invoker)的指針,并且需要在堆上為成員函數(shù)調(diào)用器分配空間。
              
              對于這種方案已經(jīng)有很多種實現(xiàn),包括在CodeProject上的實現(xiàn)方案。各種實現(xiàn)在復雜性上、語法(比如,有的和C#的語法很接近)上、一般性上有所不同。最具權(quán)威的一個實現(xiàn)是boost::function。最近,它已經(jīng)被采用作為下一個發(fā)布的C++標準版本中的一部分[Sutter1]。希望它能夠被廣泛地使用。
              
              就像傳統(tǒng)的委托實現(xiàn)方法一樣,我同樣發(fā)覺這種方法并不十分另人滿意。雖然它提供了大家所期望的功能,但是會混淆一個潛在的問題:人們?nèi)狈σ粋€語言的底層的構(gòu)造。 “成員函數(shù)調(diào)用器”的代碼對幾乎所有的類都是一樣的,在所有平臺上都出現(xiàn)這種情況是令人沮喪的。畢竟,堆被用上了。但在一些應(yīng)用場合下,這種新的方法仍然無法被接受。
              
              我做的一個項目是離散事件模擬器,它的核心是一個事件調(diào)度程序,用來調(diào)用被模擬的對象的成員函數(shù)。大多數(shù)成員函數(shù)非常簡單:它們只改變對象的內(nèi)部狀態(tài),有時在事件隊列(event queue)中添加將來要發(fā)生的事件,在這種情況下最適合使用委托。但是,每一個委托只被調(diào)用(invoked)一次。一開始,我使用了boost:: function,但我發(fā)現(xiàn)程序運行時,給委托所分配的內(nèi)存空間占用了整個程序空間的三分之一還要多!“我要真正的委托!”我在內(nèi)心呼喊著,“真正的委托只需要僅僅兩行匯編指令啊!”
              
              我并不能總是能夠得到我想要的,但后來我很幸運。我在這兒展示的代碼(代碼下載鏈接見譯者注)幾乎在所有編譯環(huán)境中都產(chǎn)生了優(yōu)化的匯編代碼。最重要的是,調(diào)用一個含有單個目標的委托(single-target delegate)的速度幾乎同調(diào)用一個普通函數(shù)一樣快。實現(xiàn)這樣的代碼并沒有用到什么高深的東西,唯一的遺憾就是,為了實現(xiàn)目標,我的代碼和標準C++ 的規(guī)則有些偏離。我使用了一些有關(guān)成員函數(shù)指針的未公開知識才使它能夠這樣工作。如果你很細心,而且不在意在少數(shù)情況下的一些編譯器相關(guān)(compiler-specific)的代碼,那么高性能的委托機制在任何C++編譯器下都是可行的。
              
              訣竅:將任何類型的成員函數(shù)指針轉(zhuǎn)化為一個標準的形式
              
              我的代碼的核心是一個能夠?qū)⑷魏晤惖闹羔樅腿魏纬蓡T函數(shù)指針分別轉(zhuǎn)換為一個通用類的指針和一個通用成員函數(shù)的指針的類。由于C++沒有“通用成員函數(shù)(geneic member function)”的類型,所以我把所有類型的成員函數(shù)都轉(zhuǎn)化為一個在代碼中未定義的CGenericClass類的成員函數(shù)。
              
              大多數(shù)編譯器對所有的成員函數(shù)指針平等地對待,不管他們屬于哪個類。所以對這些編譯器來說,可以使用reinterpret_cast將一個特定的成員函數(shù)指針轉(zhuǎn)化為一個通用成員函數(shù)指針。事實上,假如編譯器不可以,那么這個編譯器是不符合標準的。對于一些接近標準(almost-compliant)的編譯器,比如Digital Mars,成員函數(shù)指針的reinterpret_cast轉(zhuǎn)換一般會涉及到一些額外的特殊代碼,當進行轉(zhuǎn)化的成員函數(shù)的類之間沒有任何關(guān)聯(lián)時,編譯器會出錯。對這些編譯器,我們使用一個名為horrible_cast的內(nèi)聯(lián)函數(shù)(在函數(shù)中使用了一個union來避免C++的類型檢查)。使用這種方法看來是不可避免的??boost::function也用到了這種方法。
              
              對于其他的一些編譯器(如Visual C++, Intel C++和Borland C++),我們必須將多重(multiple-)繼承和虛擬(virtual-)繼承類的成員函數(shù)指針轉(zhuǎn)化為單一(single-)繼承類的函數(shù)指針。為了實現(xiàn)這個目的,我巧妙地使用了模板并利用了一個奇妙的戲法。注意,這個戲法的使用是因為這些編譯器并不是完全符合標準的,但是使用這個戲法得到了回報:它使這些編譯器產(chǎn)生了優(yōu)化的代碼。
              
              既然我們知道編譯器是怎樣在內(nèi)部存儲成員函數(shù)指針的,并且我們知道在問題中應(yīng)該怎樣為成員函數(shù)指針調(diào)整this指針,我們的代碼在設(shè)置委托時可以自己調(diào)整this指針。對單一繼承類的函數(shù)指針,則不需要進行調(diào)整;對多重繼承,則只需要一次加法就可完成調(diào)整;對虛擬繼承...就有些麻煩了。但是這樣做是管用的,并且在大多數(shù)情況下,所有的工作都在編譯時完成!
              
              這是最后一個訣竅。我們怎樣區(qū)分不同的繼承類型?并沒有官方的方法來讓我們區(qū)分一個類是多重繼承的還是其他類型的繼承。但是有一種巧妙的方法,你可以查看我在前面給出了一個列表(見中篇)——對MSVC,每種繼承方式產(chǎn)生的成員函數(shù)指針的大小是不同的。所以,我們可以基于成員函數(shù)指針的大小使用模版!比如對多重繼承類型來說,這只是個簡單的計算。而在確定unknown_inheritance(16字節(jié))類型的時候,也會采用類似的計算方法。
              
              對于微軟和英特爾的編譯器中采用不標準12字節(jié)的虛擬繼承類型的指針的情況,我引發(fā)了一個編譯時錯誤(compile-time error),因為需要一個特定的運行環(huán)境(workaround)。如果你在MSVC中使用虛擬繼承,要在聲明類之前使用 FASTDELEGATEDECLARE宏。而這個類必須使用unknown_inheritance(未知繼承類型)指針(這相當于一個假定的 __unknown_inheritance關(guān)鍵字)。例如:
              
            FASTDELEGATEDECLARE(CDerivedClass)
              
              
            class CDerivedClass : virtual public CBaseClass1, virtual public CBaseClass2 {
              
              
            // : (etc)
              
              };

              
              這個宏和一些常數(shù)的聲明是在一個隱藏的命名空間中實現(xiàn)的,這樣在其他編譯器中使用時也是安全的。MSVC(7.0或更新版本)的另一種方法是在工程中使用/vmg編譯器選項。而Inter的編譯器對/vmg編譯器選項不起作用,所以你必須在虛擬繼承類中使用宏。我的這個代碼是因為編譯器的bug才可以正確運行,你可以查看代碼來了解更多細節(jié)。而在遵從標準的編譯器中不需要注意這么多,況且在任何情況下都不會妨礙FASTDELEGATEDECLARE宏的使用。
              
              一旦你將類的對象指針和成員函數(shù)指針轉(zhuǎn)化為標準形式,實現(xiàn)單一目標的委托(single-target delegate)就比較容易了(雖然做起來感覺冗長乏味)。你只要為每一種具有不同參數(shù)的函數(shù)制作相應(yīng)的模板類就行了。實現(xiàn)其他類型的委托的代碼也大都與此相似,只是對參數(shù)稍做修改罷了。
              
              這種用非標準方式轉(zhuǎn)換實現(xiàn)的委托還有一個好處,就是委托對象之間可以用等式比較。目前實現(xiàn)的大多數(shù)委托無法做到這一點,這使這些委托不能勝任一些特定的任務(wù),比如實現(xiàn)多播委托(multi-cast delegates) [Sutter3]。
              
              靜態(tài)函數(shù)作為委托目標(delegate target)
              
              理論上,一個簡單的非成員函數(shù)(non-member function),或者一個靜態(tài)成員函數(shù)(static member function)可以被作為委托目標(delegate target)。這可以通過將靜態(tài)函數(shù)轉(zhuǎn)換為一個成員函數(shù)來實現(xiàn)。我有兩種方法實現(xiàn)這一點,兩種方法都是通過使委托指向調(diào)用這個靜態(tài)函數(shù)的“調(diào)用器(invoker)”的成員函數(shù)的方法來實現(xiàn)的。
            posted on 2007-06-08 00:18 colys 閱讀(466) 評論(0)  編輯 收藏 引用 所屬分類: C++

            国产精品成人无码久久久久久 | 亚洲色欲久久久久综合网| 久久久久女教师免费一区| 亚洲а∨天堂久久精品| 伊人久久大香线蕉av不卡| 日韩精品国产自在久久现线拍 | 国产精品99久久免费观看| 久久综合综合久久狠狠狠97色88| 久久国产精品99久久久久久老狼| 久久精品女人天堂AV麻| 久久人爽人人爽人人片AV| 亚洲狠狠久久综合一区77777| 一级做a爰片久久毛片看看| 99久久婷婷免费国产综合精品| 久久久WWW成人| 2021久久精品国产99国产精品| 精品久久久无码中文字幕天天| 亚洲国产精品无码久久一区二区 | 精品国产乱码久久久久久呢 | 精品久久久久中文字幕日本| 18岁日韩内射颜射午夜久久成人| 久久91精品国产91| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 99久久精品免费看国产一区二区三区 | 久久精品国产清自在天天线| 久久亚洲日韩精品一区二区三区| 亚洲欧美久久久久9999| 久久免费精品视频| 色狠狠久久AV五月综合| 伊人久久久AV老熟妇色| 思思久久99热免费精品6| 99久久精品九九亚洲精品| 99久久精品国内| 久久精品一本到99热免费| 久久久久久午夜精品| 欧美久久亚洲精品| 久久影院午夜理论片无码 | 久久久久久久亚洲精品| 久久久精品日本一区二区三区| 777久久精品一区二区三区无码| 国产一区二区三区久久|