前言
最近工作需要想辦法優化架構,因此設計了兩個類別各自負責自己工作,但有些資訊是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。