注:與tr1::function對象結合使用,能得到更好的效果,詳情見http://blog.csdn.net/this_capslock/article/details/38564719程序員
回調函數是基於C編程的Windows SDK的技術,不是針對C++的,程序員能夠將一個C函數直接做爲回調函數,可是若是試圖直接使用C++的成員函數做爲回調函數將發生錯誤,甚至編譯就不能經過。 編程
普通的C++成員函數都隱含了一個傳遞函數做爲參數,亦即「this」指針,C++經過傳遞一個指向自身的指針給其成員函數從而實現程序函數能夠訪問C++的數據成員。這也能夠理解爲何C++類的多個實例能夠共享成員函數可是確有不一樣的數據成員。因爲this指針的做用,使得將一個CALLBACK型的成員函數做爲回調函數安裝時就會由於隱含的this指針使得函數參數個數不匹配,從而致使回調函數安裝失敗。函數
這樣從理論上講,C++類的成員函數是不能看成回調函數的。但咱們在用C++編程時總但願在類內實現其功能,即要保持封裝性,若是把回調函數寫做普通函數有諸多不便。通過網上搜索和本身研究,發現了幾種巧妙的方法,可使得類成員函數看成回調函數使用。this
這裏採用Linux C++中線程建立函數pthread_create舉例,其原型以下:spa
類MyClass須要在本身內部開闢一個子線程來執行成員函數func()中的代碼,子線程經過調用startThread()成員函數來啓動。這裏將回調函數callback寫在了類外面,傳遞的參數是一個指向MyClass對象的指針(在pthrad_create()中由第4個參數this指定),回調函數通過強制轉換把void*變爲MyClass*,而後再調用arg->func()執行子線程的代碼。.net
這樣作的原理是把當前對象的指針看成參數先交給一個外部函數,再由外部函數調用類成員函數,之外部函數做爲回調函數,但執行的是成員函數的功能,這樣至關於在中間做了一層轉換。缺點是回調函數在類外,影響了封裝性,這裏把callback()限定爲static,防止在其它文件中調用此函數。線程
方法二:回調函數爲類內靜態成員函數,在其內部調用成員函數指針
在方法一上稍做更改,把回調函數搬到類MyClass裏,這樣就保持了封裝性。代碼以下:rest
類MyClass有了1個靜態數據成員CurMy和1個靜態成員函數callback。CurMy用來存儲一個對象的指針,充當方法一中回調函數的參數arg。callback看成回調函數,執行CurMy->func()的代碼。每次創建線程前先要調用setCurMy()來讓CurMy指向當前本身。code
這個方法的好處時封裝性獲得了很好的保護,MyClass對外只公開一個接口startThread(),子線程代碼和回調函數都被設爲私有,外界不可見。另外沒有佔用callback的參數,能夠從外界傳遞參數進來。但每一個對象啓動子線程前必定要注意先調用setCurMy()讓CurMy正確的指向自身,不然將爲其它對象開啓線程,這樣很引起很嚴重的後果。
方法三:對成員函數進行強制轉換,看成回調函數
代碼以下:
這個方法是原理是,MyClass::func最終會轉化成 void func(MyClass *this); 也就是說在原第一個參數前插入指向對象自己的this指針。能夠利用這個特性寫一個非靜態類成員方法來直接做爲線程回調函數。對編譯器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)這兩種函數指針雖然看上去很不同,但他們的最終形式是相同的,所以就能夠把成員函數指針強制轉換成普通函數的指針來看成回調函數。在創建線程時要把當前對象的指針this看成參數傳給回調函數(成員函數func),這樣才能知道線程是針對哪一個對象創建的。
方法三的封裝性比方法二更好,由於不涉及多個對象共用一個靜態成員的問題,每一個對象能夠獨立地啓動本身的線程而不影響其它對象。
暫時就列出這些方法,之後發現更好的再來補充,over
http://blog.csdn.net/this_capslock/article/details/17001003
---------------------------------------------------------------------------------------