Je sais que cela a été demandé tant de fois, et à cause de cela, il est difficile de creuser dans la cruauté et de trouver un exemple simple de ce qui fonctionne.
J'ai ça, c'est simple et ça marche pour 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);
};
Comment cela peut-il être réécrit pour EventHandler::addHandler()
fonctionner avec les deux MyClass
et YourClass
. Je suis désolé mais c'est juste la façon dont mon cerveau fonctionne, j'ai besoin de voir un exemple simple de ce qui fonctionne avant de pouvoir comprendre pourquoi / comment cela fonctionne. Si vous avez une façon préférée de faire ce travail, c'est le moment de le montrer, veuillez marquer ce code et le renvoyer.
[Éditer]
Il a été répondu mais la réponse a été supprimée avant que je puisse donner la coche. La réponse dans mon cas était une fonction basée sur un modèle. Ajout de addHandler à ceci ...
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);
}
};
Réponses:
Au lieu d'avoir des méthodes statiques et de transmettre un pointeur vers l'instance de classe, vous pouvez utiliser les fonctionnalités du nouveau standard C ++ 11:
std::function
etstd::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); } };
La
addHandler
méthode accepte maintenant unstd::function
argument, et cet "objet fonction" n'a pas de valeur de retour et prend un entier comme argument.Pour le lier à une fonction spécifique, vous utilisez
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; }
Vous devez utiliser
std::bind
lors de l'ajout du gestionnaire, car vous devez explicitement spécifier lethis
pointeur autrement implicite comme argument. Si vous disposez d'une fonction autonome, vous n'avez pas besoin d'utiliserstd::bind
:void freeStandingCallback(int x) { // ... } int main() { // ... handler->addHandler(freeStandingCallback); }
Le fait que le gestionnaire d'événements utilise des
std::function
objets, permet également d'utiliser les nouvelles fonctions lambda C ++ 11 :handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
la source
std::bind
renvoie simplement un objet (non spécifié), et lorsque vous avez terminé, vous pouvez simplement le laisser sortir de la portée. Si l'objet lié est détruit et que vous essayez d'appeler la fonction, vous obtiendrez un comportement indéfini .handler->addHandler()
, signifie que quelque part vous créez un objetEventHandler
? Bonne réponse btw, +1....., _1, _2)
et ainsi de suite.Voici une version concise qui fonctionne avec les rappels de méthode de classe et avec les rappels de fonction normaux. Dans cet exemple, pour montrer comment les paramètres sont gérés, la fonction de rappel prend deux paramètres:
bool
etint
.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);
Cela limite le code spécifique à C ++ 11 à la méthode addCallback et aux données privées dans la classe Caller. Pour moi, au moins, cela minimise le risque de faire des erreurs lors de sa mise en œuvre.
la source
Ce que vous voulez faire est de créer une interface qui gère ce code et toutes vos classes implémentent l'interface.
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 {
Notez que pour que cela fonctionne, la fonction "Callback" est non statique, ce qui, je crois, est une amélioration. Si vous voulez qu'il soit statique, vous devez le faire comme le suggère JaredC avec des modèles.
la source
Un exemple de travail complet du code ci-dessus .... pour 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
La sortie doit être:
Compiler:201103 Handler added... Result:6
la source
MyClass
etYourClass
pourraient tous les deux être dérivésSomeonesClass
dont a uneCallback
méthode abstraite (virtuelle) . VousaddHandler
accepteriez des objets de typeSomeonesClass
etMyClass
etYourClass
pouvez remplacerCallback
pour fournir leur implémentation spécifique du comportement de rappel.la source
Si vous avez des rappels avec des paramètres différents, vous pouvez utiliser des modèles comme suit:
// compilez avec: 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(); }
la source
YourClass
. Vous semblez avoir supprimé cette classe et en avoir ajouté une autreOtherClass
. De plus, la question a déjà une réponse bien reçue. Dans quelle mesure votre solution est-elle meilleure pour qu'elle vaille la peine d'être publiée?