Ich weiß, dass das so oft gefragt wurde, und deshalb ist es schwierig, durch die Scheiße zu graben und ein einfaches Beispiel dafür zu finden, was funktioniert.
Ich habe das, es ist einfach und funktioniert für 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);
};
Wie kann das umgeschrieben werden, damit EventHandler::addHandler()
sowohl mit MyClass
als auch mit YourClass
funktioniert. Es tut mir leid, aber es ist genau so, wie mein Gehirn funktioniert. Ich muss ein einfaches Beispiel sehen, was funktioniert, bevor ich verstehen kann, warum/wie es funktioniert. Wenn Sie eine Lieblingsmethode für diese Arbeit haben, können Sie sie jetzt zur Schau stellen. Markieren Sie den Code und senden Sie ihn zurück.
[Bearbeiten]
Es wurde beantwortet, aber die Antwort wurde gelöscht, bevor ich das Häkchen setzen konnte .. Die Antwort in meinem Fall war eine Templatfunktion. AddHandler zu diesem geändert ...
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);
}
};
Anstatt statische Methoden zu verwenden und einen Zeiger auf die Klasseninstanz zu übergeben, können Sie Funktionen im neuen C++ 11-Standard verwenden: std::function
und 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);
}
};
Die addHandler
-Methode akzeptiert jetzt ein std::function
-Argument, und dieses "Funktionsobjekt" hat keinen Rückgabewert und eine Ganzzahl als Argument.
Um es an eine bestimmte Funktion zu binden, verwenden Sie 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;
}
Sie müssen beim Hinzufügen des Handlers std::bind
verwenden, da Sie den ansonsten impliziten Zeiger this
explizit als Argument angeben müssen. Wenn Sie eine freistehende Funktion haben, müssen Sie std::bind
nicht verwenden:
void freeStandingCallback(int x)
{
// ...
}
int main()
{
// ...
handler->addHandler(freeStandingCallback);
}
Wenn der Event-Handler std::function
-Objekte verwendet, können auch die neuen C++ 11 Lambda-Funktionen verwendet werden.
handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
Hier ist eine prägnante Version, die mit Klassenmethoden-Rückrufen und regulären Funktionsrückrufen funktioniert. In diesem Beispiel verwendet die Rückruffunktion zwei Parameter: bool
und 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);
Dies beschränkt den C++ 11-spezifischen Code auf die addCallback-Methode und private Daten in der Klasse Caller. Für mich minimiert dies zumindest die Wahrscheinlichkeit von Fehlern bei der Implementierung.
Sie möchten eine Schnittstelle erstellen, die diesen Code behandelt, und alle Ihre Klassen implementieren die Schnittstelle.
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
{
Beachten Sie, dass die Funktion "Rückruf" für diese Funktion nicht statisch ist, wobei ich glaube, dass eine Verbesserung darstellt. Wenn Sie möchten, dass es statisch ist, müssen Sie dies tun, wie JaredC es mit Vorlagen empfiehlt.
MyClass
und YourClass
könnten beide von SomeonesClass
abgeleitet werden, die eine abstrakte (virtuelle) Callback
-Methode hat. Ihre addHandler
akzeptiert Objekte des Typs SomeonesClass
und MyClass
und YourClass
kann Callback
überschreiben, um ihre spezifische Implementierung des Rückrufverhaltens bereitzustellen.
Ein vollständiges Arbeitsbeispiel aus dem obigen Code .... für 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
Ausgabe sollte sein:
Compiler:201103
Handler added...
Result:6