Wiem, że zadawano to wiele razy i dlatego trudno jest przekopać się przez skorupę i znaleźć prosty przykład tego, co działa.
Mam to, to proste i działa przez 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);
};
Jak można to przepisać, aby EventHandler::addHandler()
działało zarówno z, jak MyClass
i YourClass
. Przykro mi, ale tak działa mój mózg. Muszę zobaczyć prosty przykład tego, co działa, zanim zrozumiem, dlaczego / jak to działa. Jeśli masz ulubiony sposób, aby to zadziałać, teraz jest czas, aby się nim pochwalić, oznacz ten kod i prześlij go z powrotem.
[edytować]
Otrzymałem odpowiedź, ale odpowiedź została usunięta, zanim mogłem zaznaczyć znacznik wyboru. Odpowiedzią w moim przypadku była funkcja szablonowa. Zmieniono addHandler na to ...
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);
}
};
Odpowiedzi:
Zamiast mieć statyczne metody i przekazywać wskaźnik do instancji klasy, możesz użyć funkcjonalności w nowym standardzie C ++ 11:
std::function
istd::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
Metody się przyjmujestd::function
argumentu, i to „obiektu funkcji” nie mają wartość powrotną i zajmuje całkowitą jako argument.Aby powiązać go z określoną funkcją, użyj
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; }
Musisz użyć
std::bind
podczas dodawania procedury obsługi, ponieważ jawnie musisz określić niejawnythis
wskaźnik w przeciwnym razie jako argument. Jeśli masz funkcję wolnostojącą, nie musisz używaćstd::bind
:void freeStandingCallback(int x) { // ... } int main() { // ... handler->addHandler(freeStandingCallback); }
Posiadanie obsługi zdarzeń używa
std::function
obiektów, umożliwia również korzystanie z nowych funkcji lambda C ++ 11 :handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
źródło
std::bind
po prostu zwraca (nieokreślony) obiekt, a kiedy skończysz, możesz po prostu pozwolić mu wyjść poza zakres. Jeśli powiązany obiekt zostanie zniszczony i spróbujesz wywołać funkcję, otrzymasz niezdefiniowane zachowanie .handler->addHandler()
oznacza, że gdzieś tworzysz obiektEventHandler
? Dobra odpowiedź przy okazji, +1....., _1, _2)
i tak dalej.Oto zwięzła wersja, która działa z wywołaniami zwrotnymi metod klas i zwykłymi wywołaniami zwrotnymi funkcji. W tym przykładzie, aby pokazać, jak obsługiwane są parametry, funkcja zwrotna przyjmuje dwa parametry:
bool
iint
.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);
Ogranicza to kod specyficzny dla C ++ 11 do metody addCallback i prywatnych danych w klasie Caller. Przynajmniej według mnie minimalizuje to ryzyko popełnienia błędów podczas wdrażania.
źródło
To, co chcesz zrobić, to stworzyć interfejs, który obsługuje ten kod i wszystkie twoje klasy implementują interfejs.
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 {
Zauważ, że aby to zadziałało, funkcja „Callback” nie jest statyczna, co uważam za ulepszenie. Jeśli chcesz, aby był statyczny, musisz to zrobić tak, jak sugeruje JaredC za pomocą szablonów.
źródło
Kompletny przykład roboczy z powyższego kodu ... dla 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
Wynik powinien być:
Compiler:201103 Handler added... Result:6
źródło
MyClass
iYourClass
oba mogą pochodzić z metodySomeonesClass
abstrakcyjnej (wirtualnej)Callback
. TwójaddHandler
przyjąłby obiektów typuSomeonesClass
aMyClass
iYourClass
może zastąpićCallback
, aby zapewnić ich realizację konkretnego zachowania zwrotnego.źródło
Jeśli masz wywołania zwrotne z różnymi parametrami, możesz użyć szablonów w następujący sposób:
// skompiluj z: 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(); }
źródło
YourClass
. Wygląda na to, że usunąłeś tę klasę i dodałeś innąOtherClass
. Co więcej, to pytanie ma już dobrze przyjętą odpowiedź. W jakim stopniu Twoje rozwiązanie jest lepsze, aby warto było je publikować?