私はこれが何度も尋ねられていることを知っています。そのため、問題を掘り下げて、何が機能するかの簡単な例を見つけることは困難です。
私はこれを持っています、それは簡単で、MyClass
...
#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()
がMyClass
とYourClass
の両方で機能するように、どのように書き換えることができますか。申し訳ありませんが、それは私の脳の仕組みです。なぜ/どのように機能するかを理解する前に、何が機能するかの簡単な例を見る必要があります。あなたがこの作品を作るためのお気に入りの方法を持っているなら、今こそそれを披露する時です、そのコードをマークアップしてそれを投稿してください。
[編集]
回答されましたが、チェックマークを付ける前に回答が削除されました。私の場合の答えは、テンプレート化された関数でした。 addHandlerをこれに変更しました...
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);
}
};
静的メソッドを持ち、クラスインスタンスへのポインタを渡す代わりに、新しいC++ 11標準の機能を使用できます: std::function
および std::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;
}
ハンドラーを追加する場合は、std::bind
を使用する必要があります。これは、暗黙的にthis
ポインターを引数として明示的に指定する必要があるためです。独立した関数がある場合は、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'; });
これは、クラスメソッドコールバックと通常の関数コールバックで機能する簡潔なバージョンです。この例では、パラメーターの処理方法を示すために、コールバック関数はbool
とint
の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のプライベートデータに制限されます。少なくとも私にとっては、これにより、実装時にミスをする可能性が最小限に抑えられます。
あなたがしたいことは、このコードを処理するインターフェースを作成し、すべてのクラスがインターフェースを実装することです。
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
{
これが機能するためには、「コールバック」機能が静的ではないことに注意してください。これはi believeが改善されたものです。静的にしたい場合は、JaredCがテンプレートで提案しているようにそれを行う必要があります。
MyClass
とYourClass
は両方とも、抽象(仮想)SomeonesClass
メソッドを持つCallback
から派生できます。 addHandler
は、タイプSomeonesClass
およびMyClass
およびYourClass
のオブジェクトを受け入れ、Callback
をオーバーライドして、コールバック動作の特定の実装を提供できます。
上記のコードからの完全な作業例... C++ 11の場合:
#include <stdlib.h>
#include <stdio.h>
#include <functional>
#if __cplusplus <= 199711L
#error This file needs at least a C++11 compliant compiler, try using:
#error $ g++ -std=c++11 ..
#endif
using namespace std;
class EventHandler {
public:
void addHandler(std::function<void(int)> callback) {
printf("\nHandler added...");
// Let's pretend an event just occured
callback(1);
}
};
class MyClass
{
public:
MyClass(int);
// Note: No longer marked `static`, and only takes the actual argument
void Callback(int x);
private:
EventHandler *pHandler;
int private_x;
};
MyClass::MyClass(int value) {
using namespace std::placeholders; // for `_1`
pHandler = new EventHandler();
private_x = value;
pHandler->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
printf("\nResult:%d\n\n", (x+private_x));
}
// Main method
int main(int argc, char const *argv[]) {
printf("\nCompiler:%ld\n", __cplusplus);
new MyClass(5);
return 0;
}
// where $1 is your .cpp file name... this is the command used:
// g++ -std=c++11 -Wall -o $1 $1.cpp
// chmod 700 $1
// ./$1
出力は次のとおりです。
Compiler:201103
Handler added...
Result:6