2013-01-07 20 views
49

これは何度も尋ねられたことがわかっています。そのため、塊を掘り起こして簡単な例を見つけるのは難しいです。C++クラスメンバコールバックの簡単な例

私はこれを持って、...

#include <iostream> 
using std::cout; 
using std::endl; 

class MyClass 
{ 
    public: 
     MyClass(); 
     static void Callback(MyClass* instance, int x); 
    private: 
     int private_x; 
}; 

class EventHandler 
{ 
    public: 
     void addHandler(MyClass* owner) 
     { 
      cout << "Handler added..." << endl; 
      //Let's pretend an event just occured 
      owner->Callback(owner,1); 
     } 
}; 

EventHandler* handler; 

MyClass::MyClass() 
{ 
    private_x = 5; 
    handler->addHandler(this); 
} 

void MyClass::Callback(MyClass* instance, int x) 
{ 
    cout << x + instance->private_x << endl; 
} 

int main(int argc, char** argv) 
{ 
    handler = new EventHandler(); 
    MyClass* myClass = new MyClass(); 
} 

class YourClass 
{ 
    public: 
     YourClass(); 
     static void Callback(YourClass* instance, int x); 
}; 

がどのようにそれはそうEventHandler::addHandler()MyClassYourClassの両方で動作します書き換えることができる。それはシンプルだし、MyClassのために働きます。申し訳ありませんが、それは私の脳が働く方法です。私はそれがなぜ/どのように機能するかを理解する前に、何がうまくいくかの簡単な例を見る必要があります。この作品を好きなように作って、それを見せる時間があるなら、そのコードをマークアップして戻してください。

[編集]

それが答えましたが、私はチェックマークを与えることができる前に答えが削除されました。 私の場合、答えはテンプレート機能でした。 addHandlerをthisに変更しました...

class EventHandler 
{ 
    public: 
     template<typename T> 
     void addHandler(T* owner) 
     { 
      cout << "Handler added..." << endl; 
      //Let's pretend an event just occured 
      owner->Callback(owner,1); 
     } 
}; 
+2

誰がテンプレート関数の例を投稿しましたか?あなたはチェックマークがありますが、私がテストしている間にあなたの答えを削除しました。それはまさに私が必要としたことでした。シンプルな関数テンプレートは、私が読んでいた他のすべての情報のシチューで失われました。あなたの答えは質問の編集として追加されました。 – BentFX

+0

私はそれがJaredCだと思います。あなたは彼を狩る必要があるかもしれない= P – WhozCraig

答えて

115

代わりに静的メソッドを持つクラスのインスタンスへのポインタの周りに渡すので、新しいC++ 11標準で機能を使用することができます。std::functionstd::bind

#include <functional> 
class EventHandler 
{ 
    public: 
     void addHandler(std::function<void(int)> callback) 
     { 
      cout << "Handler added..." << endl; 
      // Let's pretend an event just occured 
      callback(1); 
     } 
}; 

addHandler方法は今std::functionを受け入れますこの "関数オブジェクト"は戻り値を持たず、引数として整数をとります。

あなたがstd::bindを使用し、特定の機能にバインドする:

class MyClass 
{ 
    public: 
     MyClass(); 

     // Note: No longer marked `static`, and only takes the actual argument 
     void Callback(int x); 
    private: 
     int private_x; 
}; 

MyClass::MyClass() 
{ 
    using namespace std::placeholders; // for `_1` 

    private_x = 5; 
    handler->addHandler(std::bind(&MyClass::Callback, this, _1)); 
} 

void MyClass::Callback(int x) 
{ 
    // No longer needs an explicit `instance` argument, 
    // as `this` is set up properly 
    cout << x + private_x << endl; 
} 

あなたはハンドラを追加するときに明示的に引数としてそれ以外の場合は、暗黙的なthisポインタを指定する必要があるとして、std::bindを使用する必要があります。あなたは自立機能を持っている場合は、あなたがstd::bindを使用する必要はありません。

void freeStandingCallback(int x) 
{ 
    // ... 
} 

int main() 
{ 
    // ... 
    handler->addHandler(freeStandingCallback); 
} 

イベントハンドラがstd::functionオブジェクトを使用したが、また新しいC++ 11 lambda functionsを使用することが可能となります

handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; }); 
+3

ありがとうJoachim!この例は、std :: functionとstd :: bindを解読するために多くのことを行います。私は確かに将来それを使用します!私はまだラムダを得ることはできません:) – BentFX

+0

テンプレート化されたクラスは必要なものだけを行いますが、静的関数とインスタンスポインタは素敵な読みにはなりません。私はこのことをよく理解しています。私はそれを理解しています。誰が知っているのかは、未来のことかもしれません。 – BentFX

+0

@BentFXよろしくお願いします! :) –

2

あなたがしたいことは、このコードを処理するインターフェイスを作成し、すべてのクラスがインターフェイスを実装することです。

class IEventListener{ 
public: 
    void OnEvent(int x) = 0; // renamed Callback to OnEvent removed the instance, you can add it back if you want. 
}; 


class MyClass :public IEventListener 
{ 
    ... 
    void OnEvent(int x); //typically such a function is NOT static. This wont work if it is static. 
}; 

class YourClass :public IEventListener 
{ 

これは「コールバック」機能を動作させるためには、私はが改善されると信じて非静的であることに注意してください。静的にしたい場合は、JaredCがテンプレートを使用して提案するようにする必要があります。

0

MyClassおよびYourClassは、両方とも、抽象的な(仮想)Callbackメソッドを有するSomeonesClassから派生することができる。 addHandlerSomeonesClassタイプのオブジェクトを受け入れ、MyClassYourClassCallbackをオーバーライドして、コールバック動作の具体的な実装を提供することができます。

+0

私は何をしているのか私はこのアイデアでおもしろい。しかし、それは私のハンドラを使用するクラスの数が多かったため、私はそれをオプションとして見ませんでした。 – BentFX

-1

あなたは異なるパラメータでコールバックを持っている場合は、次のようにテンプレートを使用することができます。
//を使用してコンパイル:G ++ -std = C++ 11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbacksApp

#include <functional>  // c++11 

#include <iostream>  // due to: cout 


using std::cout; 
using std::endl; 

class MyClass 
{ 
    public: 
     MyClass(); 
     static void Callback(MyClass* instance, int x); 
    private: 
     int private_x; 
}; 

class OtherClass 
{ 
    public: 
     OtherClass(); 
     static void Callback(OtherClass* instance, std::string str); 
    private: 
     std::string private_str; 
}; 

class EventHandler 
{ 

    public: 
     template<typename T, class T2> 
     void addHandler(T* owner, T2 arg2) 
     { 
      cout << "\nHandler added..." << endl; 
      //Let's pretend an event just occured 
      owner->Callback(owner, arg2); 
     } 

}; 

MyClass::MyClass() 
{ 
    EventHandler* handler; 
    private_x = 4; 
    handler->addHandler(this, private_x); 
} 

OtherClass::OtherClass() 
{ 
    EventHandler* handler; 
    private_str = "moh "; 
    handler->addHandler(this, private_str); 
} 

void MyClass::Callback(MyClass* instance, int x) 
{ 
    cout << " MyClass::Callback(MyClass* instance, int x) ==> " 
     << 6 + x + instance->private_x << endl; 
} 

void OtherClass::Callback(OtherClass* instance, std::string private_str) 
{ 
    cout << " OtherClass::Callback(OtherClass* instance, std::string private_str) ==> " 
     << " Hello " << instance->private_str << endl; 
} 

int main(int argc, char** argv) 
{ 
    EventHandler* handler; 
    handler = new EventHandler(); 
    MyClass* myClass = new MyClass(); 
    OtherClass* myOtherClass = new OtherClass(); 
} 
+1

OPの問題を解決するためにあなたがしたことを説明してください。それは本当にOPの完全なコードを含める必要がありますか? OPは、彼のコードが自分の「YourClass」で動作することを望んでいました。あなたはそのクラスを削除して別の 'OtherClass'を追加したようです。さらに、質問はすでによく受け取った答えを持っています。あなたのソリューションはどれくらい遠くにあるので、投稿する価値がありますか? – honk

+0

私の投稿がより良い解決策であるとは言いませんでした。私はテンプレートの方法で "OtherClass"の使い方を示しました。 – mohDady

3

ここでは、クラスメソッドコールバックと通常の関数コールバックで動作する簡潔なバージョンを示します。この例では、パラメータの処理方法を示すために、コールバック関数はboolintという2つのパラメータをとります。

class Caller { 
    template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int)) 
    { 
    using namespace std::placeholders; 
    callbacks_.emplace_back(std::bind(mf, object, _1, _2)); 
    } 
    void addCallback(void(* const fun)(bool,int)) 
    { 
    callbacks_.emplace_back(fun); 
    } 
    void callCallbacks(bool firstval, int secondval) 
    { 
    for (const auto& cb : callbacks_) 
     cb(firstval, secondval); 
    } 
private: 
    std::vector<std::function<void(bool,int)>> callbacks_; 
} 

class Callee { 
    void MyFunction(bool,int); 
} 

//then, somewhere in Callee, to add the callback, given a pointer to Caller `ptr` 

ptr->addCallback(this, &Callee::MyFunction); 

//or to add a call back to a regular function 
ptr->addCallback(&MyRegularFunction); 

これは、C++ 11固有のコードをaddCallbackメソッドとプライベートデータのCallerクラスに制限します。私にとっては、少なくとも、これはそれを実装する際に間違いを起こす機会を最小限に抑えるものです。