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

            牽著老婆滿街逛

            嚴以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            泛化仿函數用法及參數綁定的問題

            from:http://blog.csdn.net/dog_in_yellow/archive/2007/02/15/1510538.aspx


                 《C++設計新思維》一書里的泛化仿函數從Command模式講起。Command模式主要用來降低系統中命令的調用者和執行者間的依存性。設計模式的書里面一般都采用多態的機制,調用者持有Command對象的基類接口,在此處我們稱為Command接口,Command接口不知道自己將被用于執行什么命令,一般只包含一個觸發命令執行的虛函數,假設名為Excute。各種不同的實際執行命令的Command對象從則Command接口派生,并重寫Excute虛函數。這樣調用者通過Command接口來觸發命令執行時,因為虛函數機制的關系,實際上調用的都是從Command接口派生的Command對象的Excute函數。在這樣的設計中,調用者和各種實際的命令對象互不相見,只持有一個Command接口。在C風格的設計中,一般用回調預先保存的函數指針來實現Command模式。

                泛化仿函數可以說是一種回調,但它不但可以保存函數指針,還可以處理實現了operator()的C++對象,C++對象的成員函數。具體實現方法書里面已描述得非常詳細,就不再多啰嗦,在此主要說一下項目中運用泛化仿函數的一些心得。基本如下用法:
            Functor<RETURN_TYPE,PARAM_TYPE_LIST> cmd1(...);
            Functor有兩個泛型參數,第一個為函數返回值,第二個為函數的參數列表。構造函數可接受仿函數、類成員函數以及一般函數指針。下面示例代碼演示了其用法: 

            #include "loki/functor.h"
            #include 
            <iostream>
            #include 
            <string>
            using namespace std;
            using namespace Loki;

            struct TestFunctor
            {
                
            int operator()(string str)
                
            {
                    cout 
            << str << endl;
                    
            return 0;
                }

            }
            ;

            struct TestFunctor2
            {
                
            int output(int i)
                
            {
                    cout 
            << i << endl;
                    
            return 2;
                }

            }
            ;

            void TestFunction(int i,int j)
            {
                cout 
            << i << "," << j << endl;
            }


            Functor
            <int,NullType> BindCmd1()
            {
                TestFunctor f;

                Functor
            <int,LOKI_TYPELIST_1(string> cmd1( f );
                Functor
            <int,NullType> bcmd1 = BindFirst( cmd1, "another bind cmd1" );

                
            return bcmd1;
            }


            int _tmain(int argc, _TCHAR* argv[])
            {
                
            //泛化仿函數基本用法
                
            //Functor<RETURN_TYPE, PARAM_TYPELIST> cmd(...);

                TestFunctor f;
                TestFunctor2 f2;

                
            //調用operator()仿函數----------------------------
                Functor<int,LOKI_TYPELIST_1(string> cmd1( f );
                cmd1( 
            "1" );
                
            //end of 調用operator()仿函數----------------------


                
            //調用類成員函數----------------------------------
                Functor<int,LOKI_TYPELIST_1(int> cmd2( &f2,  &TestFunctor2::output );
                cmd2( 
            2 );
                
            //end of調用類成員函數------------------------------


                
            //調用一般函數指針---------------------------------
                Functor<void,LOKI_TYPELIST_2(int,int> cmd3( TestFunction );
                cmd3( 
            3,4 );
                
            //end of調用一般函數指針---------------------------

                
            //預先綁定命令的參數的調用1
                Functor<int,NullType> bcmd1 = BindFirst( cmd1, "bind cmd1" );
                bcmd1();
                
            //end of 預先綁定命令的參數的調用1

                
            //預先綁定命令的參數的調用2
                Functor<int,NullType> bcmd1_1 = BindCmd1();
                bcmd1_1();
                
            //end of 預先綁定命令的參數的調用2


                
            return 0;
            }

                用法很簡單,上面的幾個用法都只有兩行,第一行定義泛化仿函數,第二行執行仿函數。實際運用中定義和執行一般都各在不同的地方,如Command模式一樣,即它們在時間和空間上是分離的。

                如果您使用了LOKI0.1.5的庫,在“預先綁定命令的參數的調用2”的用法中會出現運行時錯誤,這是我在項目過程中碰到的,經過分析LOKI中Functor實現的代碼,終于找到了原因。項目中實際的運用當然不是這樣,示例代碼只說明了在什么情況下運用才會出錯。如代碼所示,調用綁定了參數的仿函數時,如果已經離開了所綁定參數的作用域則會出錯。而其罪魁禍首在于對綁定的參數作了優化。

              《C++設計新思維》P123中講到為避免函數轉發的成本對函數參數作了優化,如果參數為非基本類型(非內置類型,如自定義的struct,class),則將參數類型更改為該參數的引用類型。如示例中的int operator()(string str)被優化后str參數變成string &str。而當一個引用已離開其所引用對象的作用域后,該引用會成為一個dead reference,使用了dead reference,不可避免地結局就是運行時錯誤。或許作者不許我們這么使用綁定參數,或者作者沒想到我們會這么使用綁定,但至少說明了一點,過多地優化未必是件好事。

                既然知道了為什么會出錯,那就容易解決問題了。綁定的原理是將參數保存起來,在調用的時候取出預先保存的參數傳遞給要調用的函數。如果保存的類型是值類型,那不管是否離開原參數的作用域都不會出錯。現在我們來找實現參數綁定的類定義。在Functor.h中找到class BinderFirst,該類中有一個 類型定義如下:
            typedef typename Private::BinderFirstBoundTypeStorage<
             typename Private::BinderFirstTraits<OriginalFunctor>
             ::OriginalParm1>::RefOrValue
             BoundTypeStorage;
            BoundTypeStorage即保存所綁定參數的類型定義,觀其定義可以知道該類型也是做了優化,非基本類型都變成了引用。現在我們來做一點小改動,使用參數原來的類型來保存參數,修改后的定義如下:
            typedef typename Private::BinderFirstTraits<OriginalFunctor>
               ::OriginalParm1
               BoundTypeStorage;
            修改完畢后,重新編譯運行,一切OK了。

                其實上述改動并不能解決所有問題,這種做法是使用參數的原始類型來保存參數,假如參數本身是個引用類型,那在離開了所引用對象的作用域時調用還是會出錯的。最徹底的改法是將引用參數去掉引用作為保存參數的類型。我的做法是在使用的時候做文章,不管參數是否引用類型,定義綁定的Functor時都定義成非引用類型,這樣再配合上面的改動,參數必然會使用值類型保存。如果明確地知道調用時不會離開原參數的作用域,那就不必如此了。只要心中有個底,具體用法就視個人運用的環境以及個人做法的喜好了。

            LOKI0.1.5下載地址:http://sourceforge.net/forum/forum.php?forum_id=583500

            posted on 2007-09-03 16:02 楊粼波 閱讀(499) 評論(0)  編輯 收藏 引用

            久久精品国产亚洲av影院 | 国产亚洲精品久久久久秋霞| 国产亚洲色婷婷久久99精品91| 国产精品免费久久久久电影网| 久久91这里精品国产2020| 一本久久a久久精品综合香蕉| 亚洲国产精品久久电影欧美| 国产综合久久久久久鬼色| 一本伊大人香蕉久久网手机| 97视频久久久| 999久久久国产精品| 色老头网站久久网| 久久久久免费精品国产| 成人午夜精品无码区久久| 久久er热视频在这里精品| 中文成人无码精品久久久不卡| 99久久成人国产精品免费| 亚洲国产日韩欧美久久| 久久亚洲高清观看| 久久香蕉超碰97国产精品| 开心久久婷婷综合中文字幕| 久久精品国产秦先生| 狠狠色噜噜色狠狠狠综合久久| 久久国产V一级毛多内射| 久久久国产乱子伦精品作者| 久久婷婷午色综合夜啪| 久久国产影院| 国内精品免费久久影院| 久久成人影院精品777| 99久久超碰中文字幕伊人| 漂亮人妻被黑人久久精品| 四虎国产精品成人免费久久| 亚洲日本va午夜中文字幕久久| 久久高潮一级毛片免费| 久久久黄片| 久久精品成人影院| 激情五月综合综合久久69| 久久精品国产精品亚洲| 很黄很污的网站久久mimi色| 久久国产成人| 久久久精品国产|