Я знаю, что об этом так много раз спрашивали, и из-за этого трудно покопаться в развалинах и найти простой пример того, что работает.
У меня есть такая, она простая и работает на 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 :handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
источник
std::bind
просто возвращается (неопределенный) объект, и когда вы закончите с ним, вы можете просто отпустить его из области видимости. Если связанный объект разрушен, и вы попытаетесь вызвать функцию, вы получите неопределенное поведение .handler->addHandler()
, означает, что где-то вы создаете объектEventHandler
? Хороший ответ, кстати, +1....., _1, _2)
и так далее.Вот краткая версия, которая работает с обратными вызовами методов класса и с обратными вызовами обычных функций. В этом примере, чтобы показать, как обрабатываются параметры, функция обратного вызова принимает два параметра:
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 и частными данными в классе 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 {
Обратите внимание, что для того, чтобы это работало, функция «Обратный вызов» нестатична, что, по моему мнению, является улучшением. Если вы хотите, чтобы он был статическим, вам нужно сделать это, как предлагает JaredC, с помощью шаблонов.
источник
Полный рабочий пример из приведенного выше кода .... для 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
источник
MyClass
иYourClass
оба могут быть производными отSomeonesClass
абстрактного (виртуального)Callback
метода. ВашaddHandler
будет принимать объекты типаSomeonesClass
иMyClass
иYourClass
можно переопределитьCallback
для обеспечения их конкретной реализации поведения обратного вызова.источник
Если у вас есть обратные вызовы с разными параметрами, вы можете использовать следующие шаблоны:
// компилируйте с помощью: 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(); }
источник
YourClass
. Кажется, вы удалили этот класс и добавили другойOtherClass
. Более того, на этот вопрос уже есть хорошо принятый ответ. Насколько ваше решение лучше, чтобы его стоило опубликовать?