c++ 之回調函數


我們會經常碰到需要使用回調函數的場合,比如:異步socket、定時器、windows消息處理等等。
這里將列出幾種回調函數的實現機制,分析各自的優劣以供選擇。

靜態函數靜態成員函數 作為回調函數的實現比較簡單,而且除了像 std::sort 這種地方,一般很少會用到,這里就不多做說明了。下面列出的都是將 成員函數 作為回調函數的實現。


接口類

class CallbackInterface
{
public:
    virtual void onCallback() = 0;
    ...
};

class Callee : public CallbackInterface
{
public:
    virtual void onCallback() { ... }
    ...
};

class Caller
{
public:
    void register(CallbackInterface* callback) { ... }
    void update()
    {
        ...
        callback->onCallback();
        ...
    }
    ...
};

// main
caller.register(&callee);

這種方式適用於 Callee 和 CallbackInterface 自然符合繼承語義的情況:Callee 是一種 CallbackInterface,而 Caller 作為管理者,面對的是一堆的 CallbackInterface,至於具體是哪個 Callee 在做事,又是怎么做的, Caller 並不需要知道。
如果 Callee 和 CallbackInterface 並不自然符合繼承語義,最好不要使用這種方式,不然可能會碰到下列限制:

  • 需要定義 N 個 CallbackInterface,每個 CallbackInterface 需要定義具體接口函數。
  • 一個 Callee 可能需要繼承多個 CallbackInterface,且各個 CallbackInterface 的接口函數不能同名。
  • 不支持一個 Callee 對應多個 Caller 的情況。

公共基類

class Object {...}
typedef void(Object::*CALLBACK)();    

class Callee : public Object
{
public:
    void onCallback() { ... }
    ...
}

class Caller
{
public:
    void register(Object* obj, CALLBACK callback) { ... }
    void update()
    {
        ...
        obj->*callback();
        ...
    }
    ...
}

// main
caller.register(&callee, (CALLBACK)(&Callee::onCallback));

cocos-2dx 3.0 之前的版本用的就是這種方式。
盡管它已經可以滿足大多數需要回調函數的場合,但也還是有一些顯而易見的缺點:

  • 所有的 Callee 需要繼承自一個公共基類 Object。
  • register 時需要做強制類型轉換,這使得編譯期無法對回調函數本身的參數類型和數量進行檢查,如果不匹配將導致運行時錯誤。
  • 無法在 register 時傳遞不定參數給回調函數。

std::function

class Callee
{
public:
    void onCallback() { ... }
    ...
}

class Caller
{
public:
    void register(const std::function<void()>& callback) { ... }
    void update()
    {
        ...
        callback();
        ...
    }
    ...
}

// main
caller.register(std::bind(&Callee::onCallback, callee));

對比之前的實現,這種方式幾乎解決了所有的缺點。

  • Callee 不需要繼承公共基類或者回調接口類。
  • 多個 Callee 可以綁定於多個 Caller。
  • register 時不需要進行強制類型轉換,也能在編譯期檢查回調函數類型。
  • register 時還能傳遞不定參數給回調函數。

模板

class Callee
{
public:
    void onCallback() { ... }
    ...
}

class Caller
{
public:
    void register(const CBFunctor0 & callback) { ... }
    void update()
    {
        ...
        callback();
        ...
    }
    ...
}

// main
caller.register(makeFunctor((CBFunctor0*)0,callee,&Callee::onCallback));

需要包含一個回調函數庫:http://www.tedfelix.com/software/callback.h
具體的實現原理和過程可以查看: http://www.tutok.sk/fastgl/callback.html
從使用者角度看,除了不能傳遞不定參數給回調函數,它跟 std::function 方式 幾乎一樣。
當所用編譯器不支持 c++11 特性時,可以考慮用這種方式。


肯定還有其它實現回調機制的方式,碰到的時候再加進來分析。
目前看來 std::function 方式 是一種比較完美的方案,但在實際應用中使用它也還是會碰到一些問題。
而且使用成員函數作為回調函數,還需要考慮當回調函數將被調用時,如何判斷綁定的對象是否已經被銷毀。
等等這些問題,后續將會有專門的篇幅進行探討。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM