這天是市區的行程:斯德哥爾摩。
斯德哥爾摩是瑞典的首都,瑞典的首都及最大城市,亦是斯德哥爾摩省首府。瑞典王國政府、國會以及瑞典王室的官方宮殿都設在斯德哥爾摩。(來源:維基百科)
這天是市區的行程:斯德哥爾摩。
斯德哥爾摩是瑞典的首都,瑞典的首都及最大城市,亦是斯德哥爾摩省首府。瑞典王國政府、國會以及瑞典王室的官方宮殿都設在斯德哥爾摩。(來源:維基百科)
今年228連假參加了北歐極光旅行團當作蜜月旅行(報名加利利旅行社),10天7夜的旅行,十分奇幻,但也很冷,真的建議怕冷的人發熱衣與大衣要穿夠呀!接下來幾篇網誌會記錄一些旅遊的心情與照片,避免金魚腦忘記了,看到照片還不知道去了哪裡。
這次花比較多錢參加「加利利旅行社」的北歐雪鑽極光十日,深深體會到一分錢一分貨的道理,第一個經驗的點是加利利有免費提供一人一台wifi機,讓人省去了很多爬文比較以及自己去訂wifi機或sim卡的時間(雖然我一開始不曉得有這項服務,還到處貨比三家),領隊服務也很周到,每餐都會詢問大家有沒有需要點酒水,盡量滿足大家的需求,另外若有機會也會先到飯店協助大家處理大件行李,雖然他一直說是因為北歐人動作比較慢(?)另外在車上時,也會幽默地講解一些當地的風土民情。說到講解,旅行社還提供一人一台無線電導覽機,前後兩天在市區時有邀請當地華裔導遊,他們就可以透過導覽機講解各個建築的歷史以及當地人的個性與民族性,不需要大聲嚷嚷。
| 旅行社提供的wifi機 | 
最後想介紹此行讓我驚豔的小地方,那就是土耳其航空的飛機餐餐具不是塑膠免洗餐具!而是不銹鋼餐具,而且後來發現上面還有一些清洗過的刮痕,代表真的有清洗後重複使用,這一點對我這個環保魔人來說真是大大加分!!!好感度大增!!!
希望我在這幾週能夠不偷懶把網誌好好的生出來XDD最近工作需要想辦法優化架構,因此設計了兩個類別各自負責自己工作,但有些資訊是B類別動作後,A類別需要進行更新。
最直覺想到的寫法,是讓B物件認識A物件,但是這樣A物件就跟B物件產生了關聯性(耦合)。
第二種想到寫法,是A物件需要這個資訊的時候,檢查資訊有沒有更新。但這樣A物件需要多些判斷,且每個需要資訊的地方都需要加這個判斷式,後續維護不容易。
有沒有什麼辦法是,B不需要認識A,A也不需要一直檢查呢?
最後想到的解法是:使用Callback Function!!!!!
簡單的說,一般Call Function是直接呼叫Function,而Callback function則是記得function的位址,需要時再執行該Pointer指向的函式。
C++函式指標寫法如下:
void (*func )(int, int)不過為了使用上的便利性,通常會用typedef的方式給他名字,例如:
typedef void (*FUNC )(int, int)#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。
$mkdir build
$cd build
$cmake ../ -DBUILD_PYTHON_BINDINGS=TRUE
$make -j4
*如果environment的python版本不一樣