Простой пример обратного вызова члена класса C++
Я знаю, что это было задано так много раз, и из-за этого трудно копаться в крафт и найти простой пример того, что работает.
у меня есть, это просто и это работает для 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. Мне жаль, но это просто так работает мой мозг, мне нужно увидеть простой пример того, что работает, прежде чем я смогу понять, почему/как это работает. Если у вас есть любимый способ сделать эту работу сейчас самое время, чтобы показать его, пожалуйста, разметить этот код и отправить его обратно.
[edit]
это был ответ, но ответ был удален, прежде чем я мог дать галочку.
Ответ в моем случае был шаблонной функцией. Изменен 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);
}
};
6 ответов:
вместо того, чтобы иметь статические методы и передавать указатель на экземпляр класса, вы можете использовать функциональность в новом стандарте 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); } };The
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 лямбда-функции:handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
вот краткая версия, которая работает с обратными вызовами метода класса и с регулярными обратными вызовами функций. В этом примере, чтобы показать, как обрабатываются параметры, функция обратного вызова принимает два параметра:
boolиint.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 и частными данными в вызывающем классе. Для меня, по крайней мере, это уменьшает вероятность ошибок при его реализации.
что вы хотите сделать, это интерфейс, который обрабатывает этот код и все ваши классы реализуют интерфейс.
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 с шаблонами.
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 is your .cpp file name... this is the command used: // g++ -std=c++11 -Wall -o .cpp // chmod 700 // ./вывод должен быть:
Compiler:201103 Handler added... Result:6
Если у вас есть обратные вызовы с различными параметрами вы можете использовать шаблоны следующим образом:
// compile with: 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(); }
Comments