• <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 - 32, comments - 42, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            函數指針教程

            Posted on 2009-02-12 16:08 lymons 閱讀(2808) 評論(5)  編輯 收藏 引用 所屬分類: C++C
            From 2008精選

             

            函數指針教程 原版:http://www.newty.de/fpt/index.html
            譯者:
            Lymons Lau

              導引

              1. 函數指針簡介

              2.  C C++ 函數指針語法

              3. C C++里怎么實現回調函數 ?

              4.  封裝C C++函數指針的仿函數

              5. 相關鏈接

             

               1.1 什么是函數指針 ?

               1.2  開場例子或者怎么替換Switch語句

            函數指針提供了一些極其有趣,有效和絕妙的編程技術。你能用它代替switch/if語句來實現你自己的晚綁定(late-binding)或者作為回調(callback)來使用。不幸的是可能由于它的語法比較復雜幾乎所有的電腦書籍和文檔上都講解的不多。即便如此,它們也只是做了相當簡單和膚淺的說明。而對于函數指針你只需要明白它是什么以及它的語法,因為它和一般的指針比起來從來不用關心內存的分配和釋放,所以它被使用的時候是不易產生錯誤的。但你要注意的是: 要時常問自己是否真的需要函數指針。因為雖然用它來實現晚綁定也很漂亮,但用既存的C++數據結構的話會使代碼更可讀和更簡潔。另外,晚綁定的一方面實際上就是運行期(runtime: 如果你調用了一個虛擬函數,你的程序會根據一個存儲所有函數的虛擬表(V-Table)自己來確定到底真正調用的是哪一個。這就要花費一些時間而用函數指針代替虛擬函數的話有可能會節省一些時間。BTW: 現代的編譯器在這方面都做得非常好!就那我的Borland編譯器來說這個時間就比調用一次虛擬函數能節省2%

            注:(late binding)可能來自c++的術語,也為動態(dynamic binding),它主要是來實現態機制。既是一在運行時動態確定語義的機制。

            1.1 什么是函數指針?

            函數指針就是一個指針,也就是一個指向函數地址的變量。你必須注意的是,一個正在運行的程序在主內存內獲得一段固定的空間,并且編譯出來的可執行程序代碼和代碼中的變量都駐留在這段內存里。而在這個程序代碼里的一個函數無非就是一個地址。重要的是你只要知道或者說你的編譯器/處理器,怎么來解釋一個指針指向的那段內存中的內容。

            1.2 開場例子和怎么來代替一個Switch-語句

            當你想要在程序中的某一個地方調用函數DoIt()的時候, 你只需要在源代碼的這個地方放上函數DoIt()的調用即可.那么,在編譯完這段代碼后當你的程序執行到這個地方時這個DoIt()函數就會被調用.好像看上來一切都ok.但是,如果你不知道在代碼的構建時期(build-time)哪一個函數將要被調用的話你能做什么呢?在運行期你要是決定哪一個函數要被調用的話那你需要做什么呢?這時你可能會使用一個回調函數(Callback-Function)或者你想在一堆函數列表中選擇其中的一個。然而,你也能使用switch語句,在想要調用函數的地方使用不同的分支來解決這個問題。但是,這里我們講述的是另一個方式:就是使用函數指針!

            在下面的例子中,我們能看到它的主要處理就是執行一個算術操作(共有4個算術操作)。首先是使用了switch語句來實現,另外還使用了函數指針處理這個調用。這僅僅是個處理簡單的例子,我想可能正常情況下沒有人會使用函數指針來這么作吧;-)

             
            
            //------------------------------------------------------------------------------------
            // 1.2 開場例子和怎么替代一個Switch-語句
            // 任務:通過字符'+', '-', '*' 和 '/'
            //       選擇執行一個基本的算術操作. 

            // 四個算術操作  使用swicth或者一個函數指針
            // 在運行期選擇這些函數中的一個
            float Plus    (float a, float b) { return a+b; }
            float Minus   (float a, float b) { return a-b; }
            float Multiply(float a, float b) { return a*b; }
            float Divide  (float a, float b) { return a/b; }


            // switch-語句的解決方案 - <opCode> 要選擇那一個函數的操作碼
            void Switch(float a, float b, char opCode)
            {
               
            float result;

               
            // 執行操作
               switch(opCode)
               {
                  
            case '+' : result = Plus     (a, b); break;
                  
            case '-' : result = Minus    (a, b); break;
                  
            case '*' : result = Multiply (a, b); break;
                  
            case '/' : result = Divide   (a, b); break;
               }

               cout 
            << "Switch: 2+5=" << result << endl;         // 顯示結果
            }
             

            // 函數指針的解決方案 - <pt2Func> 是一個指向帶有兩個float型參數
            // 和float型返回值的函數. 這個函數指針“指定”了那一個函數將被執行.
            void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(floatfloat))
            {
               
            float result = pt2Func(a, b);    // 調用函數指針

               cout 
            << "Switch replaced by function pointer: 2-5=";  // 顯示結果
               cout << result << endl;
            }


            // 執行樣例代碼
            void Replace_A_Switch()
            {
               cout 
            << endl << "Executing function 'Replace_A_Switch'" << endl;

               Switch(
            25/* '+' 指定了 'Plus'函數將被執行 */ '+');
               Switch_With_Function_Pointer(
            25/* 指向'Minus'函數的指針 */ &Minus);
            }

            提示:一個函數指針總是使用一個特定的標識來指向一個函數!然而,一旦你想要用一個函數指針指向很多函數,那的保證這些函數擁有相同的參數和返回值。

             

              導引

              1. 函數指針簡介

              2.  C C++ 函數指針語法

              3. C C++里怎么實現回調函數 ?

              4.  封裝C C++函數指針的仿函數

              5. 相關鏈接

             

            2.  C C++ 函數指針語法

               2.1  定義函數指針

               2.2  調用規約

               2.3  給函數指針賦值

               2.4  比較函數指針

               2.5  通過函數指針調用函數

               2.6  怎么把函數指針作為參數進行傳遞 ?

               2.7  怎么返回一個函數指針?

               2.8  怎么使用函數指針數組?

             

            2.1  定義函數指針

            在語法上, 函數指針有兩種不同的類型: 一種是指向普通函數或靜態C++成員函數的指針. 另一種是指向非靜態C++成員函數. 它們之間基本的區別就是所有指向非靜態成員函數的函數指針需要一個隱含參數: 成員函數所屬類的實例. 經常要注意的是: 這兩種類型之間的函數指針是相互不兼容的.

            因為函數指針無非就是一個變量, 所以它的定義也跟正常變量一樣定義. 在下面的例子里,我們定義了3個函數指針,分別是pt2Function, pt2Member pt2ConstMember. 它們指向的函數的參數(一個float和兩個char)和返回值都相同. C++ 的例子里,指向的函數都是必須是類TMyClass的成員函數.

            // 2.1 定義一個函數指針并初始化為NULL
            int (*pt2Function)(floatcharchar= NULL;                        // C
            int (TMyClass::*pt2Member)(floatcharchar= NULL;                // C++
            int (TMyClass::*pt2ConstMember)(floatcharcharconst = NULL;     // C++

            2.2  調用規約

            一般的情況下你不用考慮一個函數的調用規約(calling convention): 如果你沒有指定另外的規約的話編譯器是把__cdecl 作為默認的規約. 如果你想要了解的更多的話, 那就繼續往后讀吧... 我們說的這個調用規約就是告訴編譯器做一些時而,比如怎么去傳遞參數或者怎么去生成函數的名字. 某些例子里還有其它的調用規約如__stdcall, __pascal __fastcall. 這些調用規約實際上是屬于函數的標識(signature): 那么函數與和自己有些不同調用規約的函數指針之間是相互不兼容的! 對于Borland Microsoft 的編譯器規定你要指定的調用規約應該放在返回值和函數或函數名之間. 而對于GNU GCC 的編譯器是使用 __attribute__ 關鍵字: 也就是通過關鍵字__attribute__把調用規約寫在函數定義的后面并用雙括號把它括上. 如果有誰還知道更多的調用規約的話: 請讓我知道;-) 另外,你想要知道函數調用在編譯規約下是怎么工作的,請閱讀Paul Carter PC Assembly Tutorial Subprograms 這一章節.

            // 2.2 定義一個調用規約
            void __cdecl DoIt(float a, char b, char c);                             // Borland and Microsoft
            void         DoIt(float a, char b, char c)  __attribute__((cdecl));     // GNU GCC


            2.3  給函數指針賦值

            把一個函數的地址賦給一個函數指針是非常容易. 你只要知道函數和成員函數的名字. 盡管大多數編譯器需要在函數名的前面加上一個地址符& 但為了代碼的可移植行你應該這么做. 當指向成員函數的時候你還需要在該函數前面加上類名和域操作符(::). 你還要保證, 在你賦值的地方已經被允許訪問該函數.

            // 2.3 給函數指針賦值
            //     注意: 盡管你能刪掉這個地址符也能在大多數的編譯器編譯通過
            //     但是為了提高程序的移植性你應該使用這個正確的方法. 

            // C
            int DoIt  (float a, char b, char c){ printf("DoIt\n");   return a+b+c; }
            int DoMore(float a, char b, char c)const{ printf("DoMore\n"); return a-b+c; } 

            pt2Function 
            = DoIt;      // 短格式
            pt2Function = &DoMore;   // 使用地址符的正確賦值方法 

            // C++
            class TMyClass
            {
            public:
               
            int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
               
            int DoMore(float a, char b, char c) const
                     { cout 
            << "TMyClass::DoMore" << endl; return a-b+c; }; 

               
            /* more of TMyClass */
            };
             
            pt2ConstMember 
            = &TMyClass::DoMore; //使用地址符的正確賦值方法
            pt2Member = &TMyClass::DoIt; // 注意: <pt2Member> 也可以指向 &DoMore函數

            2.4  比較函數指針

            對于函數指針你也能像正常寫法一樣使用比較操作符(==, !=). 在下面的例子里,檢查pt2Function pt2Member 是否是真的等于函數DoIt TMyClass::DoMore 的地址. 相當的時候輸出一段字符串.

            // 2.4 比較函數指針 

            // C
            if(pt2Function >0){                           // 檢查是否被初始化
               if(pt2Function == &DoIt)
                  printf(
            "Pointer points to DoIt\n"); }
            else
               printf(
            "Pointer not initialized!!\n"); 

            // C++
            if(pt2ConstMember == &TMyClass::DoMore)
               cout 
            << "Pointer points to TMyClass::DoMore" << endl;

            2.5  通過函數指針調用函數

            C語言里你能顯示地使用解引用操作符*來調用一個函數. 還可以直接使用函數指針來代替你要調用函數的名字. C++ 里面.* ->* 這兩個操作符可以分別與類事例在一起使用來調用這些類的成員函數(非靜態). 如果這個調用發生這些類的成員函數里則可以使用this-指針.

            // 2.5 使用函數指針調用函數
            int result1 = pt2Function    (12'a''b');          // C偷懶格式
            int result2 = (*pt2Function) (12'a''b');          // C 

            TMyClass instance1;
            int result3 = (instance1.*pt2Member)(12'a''b');   // C++
            int result4 = (*this.*pt2Member)(12'a''b');       // C++ 如果this指針能被使用 

            TMyClass
            * instance2 = new TMyClass;
            int result4 = (instance2->*pt2Member)(12'a''b');  // C++, instance2 是一個指針
            delete instance2;

            2.6  怎么把一個函數指針作為參數進行傳遞?

            你能把一個函數指針作為一個函數的調用參數. 下列代碼顯示了怎么傳遞一個函數指針(返回值為int型,第一個參數為float型,第二,三個參數都為char型):

            //------------------------------------------------------------------------------------
            // 2.6 怎么傳遞一個函數指針 

            // <pt2Func> 是一個指向帶有一個float型和兩個int型參數以及返回值是int型的函數
            void PassPtr(int (*pt2Func)(floatcharchar))
            {
               
            int result = (*pt2Func)(12'a''b');     // 調用函數指針
               cout << result << endl;


            // 執行樣例代碼 - 'DoIt' 是在上面2.1-4定義個函數 
            void Pass_A_Function_Pointer()
            {
               cout 
            << endl << "Executing 'Pass_A_Function_Pointer'" << endl;
               PassPtr(
            &DoIt);
            }

            2.7  怎么返回函數指針 ?

            把一個函數指針作為返回值需要一個小技巧. 就像下面的例子一樣有兩種方法來返回一個帶有兩個參數和返回值的函數指針. 如果你想要返回一個指向成員函數的指針你只需改一下函數指針的定義/聲明.

            //---------------------------------------------------------------------------
            // 2.7 怎么返回一個函數指針
            //     'Plus' 和'Minus'參看前面的定義. 它們都返回一個float 和 帶有兩個float參數 

            // 直接方案: 定義了一個帶有char型參數并且返回一個指向帶有兩個float型和返回值為float
            // 型的函數. <opCode>則是決定哪個函數被返回
            float (*GetPtr1(const char opCode))(floatfloat)
            {
               
            if(opCode == '+')
                  
            return &Plus;
               
            else
                  
            return &Minus; // 如果傳遞的參數為無效時,是缺省函數
            }

            // 使用typedef的方案: 定義一個指向帶有兩個floats型和返回值是float型的函數
            typedef float(*pt2Func)(floatfloat); 

            // 定義帶有一個char型參數和返回一個上面定一個的函數指針的函數
            // <opCode> 決定那一個函數被返回
            pt2Func GetPtr2(const char opCode)
            {
               
            if(opCode == '+')
                  
            return &Plus;
               
            else
                  
            return &Minus; //如果傳遞的參數為無效時,是缺省函數


            // 執行樣例代碼
            void Return_A_Function_Pointer()
            {
               cout 
            << endl << "Executing 'Return_A_Function_Pointer'" << endl; 

               
            // 定義函數指針并初始化為NULL
               float (*pt2Function)(floatfloat= NULL;

               pt2Function
            =GetPtr1('+');   // 通過函數指針'GetPtr1'得到調用函數
               cout << (*pt2Function)(24<< endl;   // 調用該函數 

               pt2Function
            =GetPtr2('-');   //通過函數指針'GetPtr2'得到調用函數
               cout << (*pt2Function)(24<< endl;   //調用該函數
            }

            2.8  怎么使用函數指針數組?

            操作函數指針數組是非常有意思的事情. 這使得用一個索引來選擇一個函數指針變得可能. 這個語法表示起來較困難,常常導致混淆. 下面有兩種方法可以在C C++里定義并使用一個函數指針數組. 第一個方法是使用typedef, 第二個方法是直接定一個數組. 愿意使用那種方法完全取決于你.

            //--------------------------------------------------------------------------- 2.8 怎么使用函數指針數組 

            // C -----------------------------------------------------------------------
            // 類型定義: 'pt2Function' 現在能被作為一個類型來使用
            typedef int (*pt2Function)(floatcharchar);

            // 闡述一個函數指針數組是怎么工作的
            void Array_Of_Function_Pointers()
            {
               printf(
            "\nExecuting 'Array_Of_Function_Pointers'\n");

               
            // 定義一個數組并初始化每一個元素為NULL, <funcArr1> 和 <funcArr2> 是帶有
               
            // 10個函數指針的數組

               
            // 第一個方法是使用 typedef
               pt2Function funcArr1[10= {NULL};

               
            // 第二個方法是直接定義這個數組
               int (*funcArr2[10])(floatcharchar= {NULL};

               
            // 賦于函數的地址 - 'DoIt' 和 'DoMore' 的定義請參照上面2.1-4
               funcArr1[0= funcArr2[1= &DoIt;
               funcArr1[
            1= funcArr2[0= &DoMore;

               
            /* 更多的賦值 */

               
            // 使用一個索引來調用這些函數指針
               printf("%d\n", funcArr1[1](12'a''b'));         //  偷懶格式
               printf("%d\n", (*funcArr1[0])(12'a''b'));      // "正確" 的調用方式
               printf("%d\n", (*funcArr2[1])(56'a''b'));
               printf(
            "%d\n", (*funcArr2[0])(34'a''b'));
            }

            // C++ -------------------------------------------------------------------

            // 類型定義: 'pt2Member' 現在能被作為類型來使用
            typedef int (TMyClass::*pt2Member)(floatcharchar);

            // 闡述成員函數指針是怎么工作的
            void Array_Of_Member_Function_Pointers()
            {
               cout 
            << endl << "Executing 'Array_Of_Member_Function_Pointers'" << endl;

               
            // 定義一個數組并初始化每一個元素為NULL, <funcArr1> 和 <funcArr2> 是帶有
               
            // 10個函數指針的數組

               
            // 第一個方法是使用 typedef
               pt2Member funcArr1[10= {NULL};

               
            // 第二個方法是直接定義這個數組
               int (TMyClass::*funcArr2[10])(floatcharchar= {NULL};

               
            // 賦于函數的地址 - 'DoIt' 和 'DoMore' 的定義請參照上面2.1-4
               funcArr1[0= funcArr2[1= &TMyClass::DoIt;
               funcArr1[
            1= funcArr2[0= &TMyClass::DoMore;
               
            /* 更多的賦值 */

               
            // 使用一個索引來調用這些成員函數指針
               
            // 注意: 要調用成員函數則需要一個TMyClass類的實例
               TMyClass instance;
               cout 
            << (instance.*funcArr1[1])(12'a''b'<< endl;
               cout 
            << (instance.*funcArr1[0])(12'a''b'<< endl;
               cout 
            << (instance.*funcArr2[1])(34'a''b'<< endl;
               cout 
            << (instance.*funcArr2[0])(89'a''b'<< endl;
            }


              導引

              1. 函數指針簡介

              2.  C C++ 函數指針語法

              3. C C++里怎么實現回調函數 ?

              4.  封裝C C++函數指針的仿函數

              5. 相關鏈接

             

            3.  怎么在 C C++中實現回調

               3.1  回調函數的概念

               3.2  怎么在C 中實現回調?

               3.3  使用qsort的例子

               3.4  怎么在C++ 中實現靜態成員函數的回調?

               3.5 怎么在C++ 中實現靜態成員函數的回調?

                  A: 把類實例的指針作為附加參數進行傳遞

                  B: 把類實例的指針存儲在全局變量里

            3.1  回調函數的概念

            在回調函數的概念中當然少不了函數指針這東西. 如果你不知道怎么使用函數指針的話你可以去看一下函數指針簡介 這一章. 我將使用著名的排序函數qsort來給大家解釋回調函數的概念. 這個函數可以根據用戶指定的排序方式來排列一個區域中的很多項目之間的順序. 這個區域中包含的項目可以是任何類型; 它是通過void-類型的指針被傳遞到這個排序函數里. 另外該類型項目的大小和這個區域中項目的數量也要被傳遞到排序函數里.現在的問題是: 這個排序函數在不知道項目類型的任何信息的情況下怎么實現排序功能的呢? 這個答案非常簡單: 就是這個函數要接收一個指向帶有兩個參數是void型項目指針的比較函數的函數指針, 這個比較函數則是來估算兩個項目之間的順序并返回一個int型的結果代碼. 因此每一次這個排序算法需要決定兩個項目之間的順序時則僅僅是通過函數指針來調用這個比較函數即可: 這就是回調!

            3.2  怎么在C里實現回調?

            下面是函數 qsort 的聲明:

               void qsort(void* field, size_t nElements, size_t sizeOfAnElement,
                                int(_USERENTRY *cmpFunc)(const void*, const void*));

            field 指向要被排序的那個域中的第一個元素, nElements 是這個域里的項目數量, sizeOfAnElement 則是用字節表示的一個項目的大小并且 cmpFunc 指向比較函數的函數指針. 這個比較函數帶有兩個void-型指針的參數并返回一個型的返回值int. 在函數的定義里怎么把函數指針作為一個參數來使用的語法看起來有一些陌生. 只要看看, 怎么定義一個函數指針這一章你就能發現它完全就是相同的. 執行一個 回調就像普通函數被調用那樣簡單: 你只需要使用函數指針的名字而不是那個函數的名字. 就像下面顯示的那樣. 注意: 下面的代碼中除了函數指針所有的參數都被省略了因為我們只是關注跟函數指針相關的事情.

             
            
               void qsort(  , int(_USERENTRY *cmpFunc)(const void*const void*))
               {
                  
            /* 排序算法  - 注意: item1 和 item2 是 void-型的指針 */ 

                  
            int bigger=cmpFunc(item1, item2);  // 做一個回調 

                  
            /* 使用上面的結果 */
               }

            3.3  qsort的使用例子

            在下面的例子里排序 floats 型的項目.

             
            
             //-----------------------------------------------------------------------------------------
               
            // 3.3 通過qsort排序函數的方法在C 中實現回調

               #include 
            <stdlib.h>        // due to:  qsort
               #include <time.h>          // randomize
               #include <stdio.h>         // printf

               
            // 排序算法的比較函數
               
            // 兩個被比較的項目的類型都是 void-指針, 先作轉換后作比較
               int CmpFunc(const void* _a, const void* _b)
               {
                  
            // you've got to explicitly cast to the correct type
                  const float* a = (const float*) _a;
                  
            const float* b = (const float*) _b;

                  
            if(*> *b) return 1;              // first item is bigger than the second one -> return 1
                  else
                     
            if(*== *b) return  0;         // equality -> return 0
                     else         return -1;         // second item is bigger than the first one -> return -1
               }

               
            // 使用qsort()的例子
               void QSortExample()
               {
                  
            float field[100];

                  ::randomize();                     
            // 初始化隨機數生成器
                  for(int c=0;c<100;c++)             // 給域中的每個元素設定隨機值
                     field[c]=random(99);

                  
            // 用 qsort()進行排序
                  qsort((void*) field, /*項目的數量*/ 100/*一個項目的大小*/ sizeof(field[0]),
                        
            /*比較函數*/ CmpFunc);

                  
            // 排完序后顯示前10個元素
                  printf("The first ten elements of the sorted field are \n");
                  
            for(int c=0;c<10;c++)
                     printf(
            "element #%d contains %.0f\n", c+1, field[c]);
                  printf(
            "\n");
               }

            3.4  實現 C++ 靜態成員函數的回調?

            這跟在C里的實現是完全一樣. 靜態成員函數不需要調用類對象并就像C函數一樣擁有相同標識,相同的調用規約,調用參數以及返回值.

            3.5 實現 C++ 靜態成員函數的回調?

            封裝方法

            指向非靜態成員的指針和普通的C指針是不一樣的,因為它們需要傳遞一個類對象的this-指針. 而且普通的函數指針和非靜態成員函數有些不同并存在不兼容的標識(signatures)! 如果你想要回調一個指定類的成員函數那你得把你的代碼從普通的函數指針改成一個指向成員函數的指針. 但是如果你想要回調一個任意類的非靜態成員函數那你能怎么做呢? 這有一點兒困難. 你需要寫一個靜態的成員函數作為包裝函數. 一個靜態成員函數擁有和C函數一樣的標識! 然后你要把要調用的成員函數的類對象指針強轉為void* 并作為附加參數或者全局變量傳遞到包裝函數里. 有一點比較重要,如果你使用全局變量時你必須得保證這個指針總是指向一個正確的類對象! 當然你也得為成員函數傳遞那些調用參數. 這個包裝函數把void指針強轉為對應類的實例的指針并調用這個成員函數. 在后面你能看到兩個例子.

            A: 作為附加參數來傳遞類實例的指針

            函數 DoItA 利用TClassA類(包含有一個回調)的對象作一些事情. 因此指向靜態包裝函數TClassA::Wrapper_To_Call_Display的指針要被傳遞到函數DoItA. 這個包裝函數是一個回調函數. 你也能隨便寫一個類似于TClassA的類并在函數DoItA中使用當然前提是那些類立提供了包裝函數. 注意:利用回調函數作為接口的設計方案會比下面使用全局變量的那個方案要有用的多.

               //-----------------------------------------------------------------------------------------
               
            // 3.5 例A: 使用附加參數來回調成員函數
               
            // 任務: 函數 'DoItA'中回調成員函數'Display'. 因此要使用包裝函數
               
            //       'Wrapper_To_Call_Display'.

               #include 
            <iostream.h>   // due to:   cout

               
            class TClassA
               {
               
            public:

                  
            void Display(const char* text) { cout << text << endl; };
                  
            static void Wrapper_To_Call_Display(void* pt2Object, char* text);

                  
            /* more of TClassA */
               };

               
            // 靜態成員函數能回調成員函數Display()
               void TClassA::Wrapper_To_Call_Display(void* pt2Object, char* string)
               {
                   
            // 顯示地強轉為TClassA類的指針
                   TClassA* mySelf = (TClassA*) pt2Object;

                   
            // 調用成員函數
                   mySelf->Display(string);
               }


               
            // 隱含地做回調
               
            // 注意: 當然這個函數也能作為成員函數
               void DoItA(void* pt2Object, void (*pt2Function)(void* pt2Object, char* text))
               {
                  
            /* do something */

                  pt2Function(pt2Object, 
            "hi, i'm calling back using a argument ;-)");  // make callback
               }


               
            // 執行例程
               void Callback_Using_Argument()
               {
                  
            // 1. TClassA類對象的初始化
                  TClassA objA;

                  
            // 2. 為對象<objA>調用函數'DoItA' 
                  DoItA((void*&objA, TClassA::Wrapper_To_Call_Display);
               }

            B: 把類實例的指針作為全局變量

            函數DoItB利用TClassA類(包含有一個回調)的對象作一些事情. 因此指向靜態包裝函數TClassB::Wrapper_To_Call_Display的指針要被傳遞到函數DoItA. 這個包裝函數是一個回調函數. 你也能隨便寫一個類似于TClassA的類并在函數DoItA中使用當然前提是那些類立提供了包裝函數. 注意: 這個方案在已經存在的回調接口不會被改變的前提下才會比較有用,但因為使用全局變量可能會非常危險而且還可能到使嚴重錯誤所以這并不是一個很好的方案.

             
            
              //-----------------------------------------------------------------------------------------
               
            // 3.5 例B: 使用全局變量回調成員函數
               
            // 任務: 函數 'DoItB'中回調成員函數'Display'. 因此要使用包裝函數
               
            //       'Wrapper_To_Call_Display'..

               #include 
            <iostream.h>   // due to:   cout

               
            void* pt2Object;        // 一個可以指向任意類的全局變量

               
            class TClassB
               {
               
            public:

                  
            void Display(const char* text) { cout << text << endl; };
                  
            static void Wrapper_To_Call_Display(char* text);

                  
            /* more of TClassB */
               };


               
            // 靜態成員函數能回調成員函數 Display()
               void TClassB::Wrapper_To_Call_Display(char* string)
               {
                   
            // 顯示地將全局變量 <pt2Object> 強轉成類TClassB的指針
                   
            // 警告: <pt2Object> 必須指向一個有效的類對象!
                   TClassB* mySelf = (TClassB*) pt2Object;
             

                   
            // call member
                   mySelf->Display(string);
               }


               
            // 隱含地做回調
               
            // 注意: 當然這個函數也能作為成員函數
               void DoItB(void (*pt2Function)(char* text))
               {
                  
            /* do something */

                  pt2Function(
            "hi, i'm calling back using a global ;-)");   // make callback
               }

               
            // execute example code
               void Callback_Using_Global()
               {
                  
            // 1.  TClassB類對象的初始化
                  TClassB objB;


                  
            // 2. 對在靜態包裝函數中要使用的全局變量進行賦值
                  
            // 重要: 一定不要忘記作這一步
                  pt2Object = (void*&objB;


                  
            // 3. call 'DoItB' for <objB>
                  DoItB(TClassB::Wrapper_To_Call_Display);
               }

              導引

              1. 函數指針簡介

              2.  C C++ 函數指針語法

              3. C C++里怎么實現回調函數 ?

               4.  封裝C C++函數指針的仿函數

              5. 相關鏈接

            4. 封裝C C++函數指針的仿函數

               4.1  什么是仿函數 ?

               4.2  怎么實現仿函數 ?

               4.3  使用仿函數的例子

            4.1  什么是仿函 ?

            仿函是一個具有狀態的函. C++ 你能看到它一個用一到個私有成員來儲這狀態類來,并利用重的操作符()來執行本身的操作. 仿函是利用模版(templates)和多(polymorphism)的概念C C++ 的函. 你能構建一個任意的成的指列表并通相同的接口來調用它,但不干擾它或者一個例的指的需求. 僅僅是要求所有的函要保持相同的返回型和調參數. 候仿函通常稱為閉(closures). 你也能用仿函數來實現調.

            4.2  怎么實現仿函數 ?

            首先你需要一個提供了一個叫Call的虛函數的基類TFunctor或者一個能調用成員函數的虛擬重載操作符 ().是選擇重載操作符還是函數Call當然是隨便你自己的喜好了. 從這個基類你能派生一個模版 TSpecificFunctor ,在構造函數里用一個對象的指針和一個函數指針來初始化.這個派生類重載基類中的函數Call/或操作符(): 在這個重載的版本里,你能利用存儲的函數指針和類對象的指針來調用成員函數. 如果你忘了怎么去使用函數指針你可以參考章節函數指針簡介.

             
            
              //-----------------------------------------------------------------------------------------
               
            // 4.2 怎么實現仿函數

               
            // 抽象基類
               class TFunctor
               {
               
            public:

                  
            // 兩個用來調用成員函數的虛函數
                  
            // 派生類將使用函數指針和類對象指針來實現函數調用
                  virtual void operator()(const char* string)=0;  // call using operator
                  virtual void Call(const char* string)=0;        // call using function
               };

               
            // 派生模版類
               template <class TClass> class TSpecificFunctor : public TFunctor
               {
               
            private:
                  
            void (TClass::*fpt)(const char*);   // pointer to member function
                  TClass* pt2Object;                  // pointer to object

               
            public:

                  
            // 構造函數- 把類對象指針和函數指針
                  
            // 存儲在兩個私有變量中
                  TSpecificFunctor(TClass* _pt2Object, void(TClass::*_fpt)(const char*))
                     { pt2Object 
            = _pt2Object;  fpt=_fpt; };

                  
            // 重載操作符 "()"
                  virtual void operator()(const char* string)
                   { (
            *pt2Object.*fpt)(string);};              // execute member function

                  
            // 重載函數 "Call"
                  virtual void Call(const char* string)
                    { (
            *pt2Object.*fpt)(string);};             // execute member function
               };

            4.3  仿函數的使用列

            在下面的例子里我們有兩個類,包括返回值是(void)和參數為(const char*)的函數Display的一個簡單實現.這里我們創建兩個TFunctor類的指針數組并且用封裝了類對象指針和TClassA TClassB的成員函數指針的TSpecificFunctor類來初始化這個數組中的元素. 而后我們使用仿函數數組來分別調用這兩個成員函數. 類對象并不直接調用函數但你得保證你的操作不能干擾這兩個類的操作!

               //-----------------------------------------------------------------------------------------
               
            // 4.3 仿函數的使用例子

               
            // 類A
               class TClassA{
               
            public:

                  TClassA(){};
                  
            void Display(const char* text) { cout << text << endl; };

                  
            /* more of TClassA */
               };

               
            // 類 B
               class TClassB{
               
            public:

                  TClassB(){};
                  
            void Display(const char* text) { cout << text << endl; };

                  
            /* more of TClassB */
               };

               
            // 主程序
               int main(int /*argc*/char* /*argv[]*/)
               {
                  
            // 1. TClassA 和TClassB的實例
                  TClassA objA;
                  TClassB objB;

                  
            // 2. 實例化TSpecificFunctor 對象 
                  
            //    a ) 封裝指向TClassA類成員的函數指針的仿函數
                  TSpecificFunctor<TClassA> specFuncA(&objA, &TClassA::Display);

                  
            //    b) 封裝指向TClassB類成員的函數指針的仿函數
                  TSpecificFunctor<TClassB> specFuncB(&objB, &TClassB::Display);
             

                  
            // 3. 聲明基類TFunctor指針的數組, 并初始化
                  TFunctor* vTable[] = { &specFuncA, &specFuncB };


                  
            // 4. 不需要類對象就可以調用成員函數
                  vTable[0]->Call("TClassA::Display called!");        // via function "Call"
                  (*vTable[1])   ("TClassB::Display called!");        // via operator "()"


                  
            // hit enter to terminate
                  cout << endl << "Hit Enter to terminate!" << endl;
                  cin.
            get();

                  
            return 0;
               }


              導引

              1. 函數指針簡介

              2.  C C++ 函數指針語法

              3. C C++里怎么實現回調函數 ?

               4.  封裝C C++函數指針的仿函數

              5. 相關鏈接

            5.相關鏈接

               5.1  介紹函數指針

               5.2  回調和仿函數

               5.3  其他項

            5.1  介紹函數指針

            • Using Member Function Pointers  一個borland社區的詳細文章. 原本是為了給產品BC3.1提供支持但實際上它是平臺獨立的并且一直在維護更新.  [文章]
            • Classes having Pointers to Members C++注解》的第15章作者Frank Brokken.  [教程]
            • C++ FAQ-Lite  新聞組news.comp.lang.c++FAQ. 該鏈接指向的是成員函數指針那部分.  [FAQ]
            • Pointing to Class Members  一個較短但很有用的文章,闡述了怎么使用成員函數指針.  [教程]
            • Declaring Function Pointers and Implementing Callbacks  關于C的函數指針的比較短小的文章. 里面也描述了函數標識的調用規約.  [教程]
            • Notes on Function Pointers   為了闡明通用編程(generic programming)的思想的一篇簡要教程: 分割算法和數據. 容器類的使用函數指針的例子, 例如. 調用用戶指定的函數為每一個容器元素. [教程]

            5.2  回調和仿函數

            5.2  其他項

            • PC Assembly Tutorial   關于匯編的一篇很好的教程. 這里你能了解到一個函數調用是怎么工作的. 另外還有調用規約以及匯編和C代碼的之間的交互. [教程]
            • The Virtual Function Mechanism  關于使用Microsoft's Visual C++匯編器的虛函數機制一篇簡要介紹 [文章]

             

            Feedback

            # re: 函數指針教程  回復  更多評論   

            2010-07-24 17:31 by MaureenWiley20
            If you want to buy real estate, you would have to get the <a href="http://bestfinance-blog.com/topics/home-loans">home loans</a>. Moreover, my father always utilizes a commercial loan, which supposes to be really firm.

            # re: 函數指針教程  回復  更多評論   

            2010-08-05 21:01 by course work
            Looking for extra high quality and the best? You an opportunity to order coursework online at trustworthy custom writing firm.

            # re: 函數指針教程  回復  更多評論   

            2010-08-25 11:07 by ringtones
            We do opine that the bollywood ringtones would be acquirable at get ringtones services and this is truth.

            # re: 函數指針教程  回復  更多評論   

            2010-09-16 14:17 by wars essay
            Thare’s no other good way to have excellent mark than to make the research papers about this good topic and that is, also, good to order the United States essays paper in the essay papers writing service.
            我的個人簡歷第一頁 我的個人簡歷第二頁
            久久久久久久人妻无码中文字幕爆| 久久久精品免费国产四虎| 蜜臀久久99精品久久久久久| 久久久久亚洲爆乳少妇无| 国产91久久精品一区二区| 久久精品国产秦先生| 亚洲精品国产成人99久久| 中文字幕精品久久| 精品久久久中文字幕人妻| 色天使久久综合网天天| 精品久久久久一区二区三区 | 丁香五月网久久综合| 男女久久久国产一区二区三区| 久久人妻AV中文字幕| 中文字幕久久久久人妻| 亚洲va久久久噜噜噜久久男同| 综合久久国产九一剧情麻豆| 人妻无码久久一区二区三区免费 | 亚洲国产二区三区久久| www.久久热.com| 超级碰久久免费公开视频| 国产成人久久精品麻豆一区| 精品久久久久久国产三级| 久久综合一区二区无码| 欧美伊人久久大香线蕉综合| 久久综合噜噜激激的五月天| 精品久久久久久国产| 久久久久国产精品三级网| 久久久久精品国产亚洲AV无码 | 国产精品久久久久9999| 国内精品欧美久久精品| 久久午夜夜伦鲁鲁片免费无码影视| 久久人人爽人人爽人人av东京热| 久久国产免费观看精品3| 国产精品伊人久久伊人电影| 99久久国产亚洲综合精品| 69久久夜色精品国产69| 国产精品99久久久精品无码 | 国产精品美女久久久网AV| 久久久亚洲欧洲日产国码是AV| 97久久精品人人做人人爽|