1. 程式人生 > >[C++] 回調函數(轉)

[C++] 回調函數(轉)

pre ++ urn 角度 style inter 其它 特化 術語

什麽是回調(Callback)函數

回調函數(Callback Function)是怎樣一種函數呢?

函數是用來被調用的,我們調用函數的方法有兩種:

直接調用:在函數A的函數體裏通過書寫函數B的函數名來調用之,使內存中對應函數B的代碼得以執行。這裏,A稱為“主叫函數”(Caller),B稱為“被叫函數”(Callee)。

間接調用:在函數A的函數體裏並不出現函數B的函數名,而是使用指向函數B的函數指針p來使內存中屬於函數B的代碼片斷得以執行——聽起來很酷,是吧。

比起直接調用來,間接調用的確麻煩,那為什麽還要使用間接調用呢?原因很簡單——直接調用把函數名都寫進函數體了,經過編譯器那麽一編譯,板上釘釘,A

註定調用的是B了,這樣的程序只能按照程序員事先設計好的流程執行下去,太呆板了。此時,間接調用的巨大靈活性就顯現出來了。想一想,如果p是函數A的一個參數(參數是變量,是變量就可以變嗎!),那麽程序的最終用戶完全可以通過操作來改變p的指向——這樣,A在通過p調用函數的時候就有機會調用到不同的函數,這樣程序的實用性和擴展性就強多了。

如果你能明白上面一段話,OK,那麽你已經明白回調函數的75%了——因為被間接調用的函數就是回調函數本身,而間接調用是使用回調函數的“第二步”。

“第一步”又是什麽呢?讓我們仔細想想,A通過p來調用B(或者調用其它函數),p是一個指針——空指針是不能用的!所以,“第一步”就是聲明函數指針變量p

並且為它賦值——即將p綁定到一個將被間接調用的函數上。

這樣,使用回調函數的完整流程就成了這樣:

聲明函數指針p -> 向函數指針p賦值,使之指向函數B -> p作為參數傳給函數A -> 函數A通過指針p調用函數B -> 函數B的函數體得以執行。

現在一個令人迷惑的問題擺在眼前了——Callback這個詞裏有一個back,上面這個過程哪裏體現出“回”了呢?實際上是這樣的:

函數指針p的來路我們不必去追究——它可能是一個全局變量(大部分情況下是這樣),也可能是一個臨時變量——對p賦值卻是一件著實重要的事情,除了在聲明它的時候對它初始化,我們只能在某個函數中去執行對p

進行賦值的操作了,我們姑且管這個函數叫K。現在閉上眼睛跟我想,函數K和函數B處在同一個程序模塊(比如一個類或者一個DLLModule_1中,而函數A處在另一個程序模塊Modules_2中,把p想象成一條短信。Module_1發短信pModule_2,短信p的內容是“函數B的內存地址是0XFF1314521EE”,Module_2收到短信後使用這個地址,結果相當於給Module_2“回了個電話”。所以,一個“回”字,是站在包含了函數KB的模塊Module_1的角度上去看而得出的結果。

現在想想,臺灣的IT同仁保留了Callback一詞的英語詞條直譯,就叫“回呼函數”,真是高明。將Callback特化成計算機專業術語、叫做“回調”真不知道是誰的主意:p

技術分享圖片

小牛試刀

咱們老規矩,貼段代碼放在這兒供大家拍磚玩兒。

//----------------------------------------------
//                水之真諦
// http://blog.csdn.net/FantasiaX
//----------------------------------------------

#include <iostream>

// 聲明一個函數指針,返回值為void,接受一個整形參數
typedef void (*FunctionPointer)( ); 

void Function_A( );
void Function_B( FunctionPointer );
void Function_C( );
void Function_D( );

// 回調過程的原始發起者
void Function_A( ) 
{
         FunctionPointer p = 0;

         std::cout << "Please give me a choice, 1 or 2." << std::endl;
         int choice = 0;
         std::cin >> choice;
         if(choice == 1)
                   p = &Function_C;
         else if(choice ==2 )
                   p = &Function_D;
         else
                   return;
         Function_B( p );
}

// 回調過程中的“主叫函數”,Caller
void Function_B(FunctionPointer p)
{
         p( );
}

// 回調過程中的“被叫函數”,Callee
// 也就是回調函數本身,回調函數-1
void Function_C( )
{
         std::cout << "I am Function_C. Merry Christmas!" << std::endl;
}

//回調函數-2
void Function_D( )
{
         std::cout << "I am Function_D. Happy New Year!" << std::endl;
}


int main(int argc, char *argv[])
{
         Function_A( );
         return 0;
}

法律聲明:本文章受到知識產權法保護,任何單位或個人若需要轉載此文,必需保證文章的完整性(未經作者許可的任何刪節或改動將視為侵權行為)。若您需要轉載,請務必註明文章出處為CSDN以保障網站的權益;請務必註明文章作者為劉鐵猛(http://blog.csdn.net/FantasiaX ),並向[email protected]發送郵件,標明文章位置及用途。轉載時請將此法律聲明一並轉載,謝謝!

[C++] 回調函數(轉)