C++ CLASS member callback proste przykłady
Wiem, że pytano o to tyle razy, i dlatego trudno jest przekopać się przez cruft i znaleźć prosty przykład tego, co działa.
Mam to, to proste i działa na 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 MyClass
, jak i YourClass
. Przepraszam, 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ło teraz jest czas się pochwalić, proszę o oznaczenie kodu i odesłanie go z powrotem.
[edit]
Odebrano, ale odpowiedź została usunięta, zanim zdążyłem podać znacznik. Odpowiedzią w moim przypadku była funkcja template. Zmieniłem addhandlera...
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 answers
Zamiast statycznych metod i przekazywania wskaźnika do instancji klasy, można użyć funkcjonalności w nowym standardzie C++11: std::function
oraz 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);
}
};
Metoda addHandler
akceptuje teraz argument std::function
, A Ten "obiekt funkcji" nie ma zwracanej wartości i przyjmuje liczbę całkowitą jako argument.
Aby powiązać go z określoną funkcją, należy użyć 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 obsługi, ponieważ wyraźnie musisz określa jako argument wartość domyślną this
. 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 obiektów std::function
, umożliwia również korzystanie z nowych funkcji lambda C++11 :
handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 04:50:13
Oto zwięzła wersja, która działa z wywołaniami zwrotnymi metody klasy oraz z wywołaniami zwrotnymi funkcji regularnych. W tym przykładzie, aby pokazać, jak parametry są obsługiwane, funkcja callback pobiera dwa parametry: bool
i 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);
To ogranicza kod specyficzny dla C++11 do metody addCallback i prywatnych danych w klasie wywołującej. Dla mnie przynajmniej minimalizuje to szansę na popełnienie błędów przy jego wdrażaniu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-13 04:07:36
To, co chcesz zrobić, to stworzyć interfejs, który będzie obsługiwał ten kod i wszystkie Twoje klasy zaimplementują ten 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, couważam, że jest ulepszeniem. Jeśli chcesz, aby było statyczne, musisz zrobić to tak, jak sugeruje JaredC z szablonami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 03:29:24
MyClass
i YourClass
mogą być wyprowadzone z SomeonesClass
, która ma metodę abstrakcyjną (wirtualną) Callback
. Twój addHandler
zaakceptuje obiekty typu SomeonesClass
i MyClass
i YourClass
może nadpisać Callback
, aby zapewnić ich specyficzną implementację zachowania wywołania zwrotnego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 03:30:44
Kompletny działający przykład 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
Wyjście powinno być:
Compiler:201103
Handler added...
Result:6
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-11 21:32:35
Jeśli masz wywołania zwrotne o różnych parametrach, możesz użyć szablonów w następujący sposób:
// 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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-09 15:54:50