前言
最近工作需要想辦法優化架構,因此設計了兩個類別各自負責自己工作,但有些資訊是B類別動作後,A類別需要進行更新。
最直覺想到的寫法,是讓B物件認識A物件,但是這樣A物件就跟B物件產生了關聯性(耦合)。
第二種想到寫法,是A物件需要這個資訊的時候,檢查資訊有沒有更新。但這樣A物件需要多些判斷,且每個需要資訊的地方都需要加這個判斷式,後續維護不容易。
有沒有什麼辦法是,B不需要認識A,A也不需要一直檢查呢?
最後想到的解法是:使用Callback Function!!!!!
什麼是Callback Function?
簡單的說,一般Call Function是直接呼叫Function,而Callback function則是記得function的位址,需要時再執行該Pointer指向的函式。
常見的Callback Function
- std::sort
- openCV MouseCallback
- gstreamer AppSink
Callback Function怎麼用?
Function Pointer
C++函式指標寫法如下:
void (*func )(int, int)
不過為了使用上的便利性,通常會用typedef的方式給他名字,例如:
typedef void (*FUNC )(int, int)
宣告及使用Callback Function
#include <iostream> using namespace std; typedef void (*CALLBACK)( int, int, void* ); class A{ public: A( void ); // constructor static void UpdateCallback( int, int, void* ); // callback function pointer pointed to void PrintMember( void ) const; // show the member private: int a; int b; }; class B{ public: B( void ); // constructor void RegisterCallback( CALLBACK, void* ); // register callback and the callee void CallFunction( void ); // call by main private: CALLBACK pFunc; void *m_pCallee; }; A::A( void ) // constructor { a = 0; b = 0; } void A::UpdateCallback( int inputA, int inputB, void *Self ) // callback function pointer pointed to { // cast user data to A A* pA = (A*)Self; // update with input value pA->a = inputA; pA->b = inputB; } void A::PrintMember( void ) const // show the member { cout << "a = " << a << endl; cout << "b = " << b << endl; } B::B( void ) // constructor { // do nothing } void B::RegisterCallback( CALLBACK FuncCB, void* pCallee ) // register callback and the callee { pFunc = FuncCB; m_pCallee = pCallee; } void B::CallFunction( void ) // call by main { cout << "execute function pointer" << endl; pFunc( 2, 3, m_pCallee ); } int main( void ) { A instanceA; B instanceB; // register callback to B instanceB.RegisterCallback( instanceA.UpdateCallback, &instanceA ); cout << "Before run B::CallFunction: " << endl; instanceA.PrintMember(); instanceB.CallFunction(); cout << "After run B::CallFunction: " << endl; instanceA.PrintMember(); return 0; }
另外要注意實作功能的Callback Function必須是static。
(註:C++11以後用function跟bind好像就可以不用宣告為static)
註冊Callback Function後,B物件執行CallFunction的時候,就會執行Callback指向的函式,也就是A的UpdateCallback,並將Callee傳入(在這個情境下是instanceA的pointer),在UpdateCallback中,會將void pointer轉型成class A的pointer,並將值填入A的pointer中。
可以發現class B並不需要知道class A(只需要傳入一個void pointer),也就是說透過Callback Function的機制,就達成B類別不需要認識A類別,就可以叫A做事情的目的!
另外以程式碼擴展性來說,Callback Function增加了更多彈性~如果我後續修改需求,有一個新的類別C,也是要進行類似的動作(比如說使用B傳入兩個int進行相加,再存進member),那我只要在C類別中實作Callback Function,然後註冊進B就可以了,完全不需要修改B。