使用类成员的 C + + 回调

我知道这个问题已经被问过很多次了,正因为如此,我们很难挖掘出一个简单的例子来说明什么是有效的。

我有这个,它很简单,它为 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()将与 MyClassYourClass工作。我很抱歉,但这只是我的大脑工作的方式,我需要看到一个简单的例子,什么工作之前,我可以理解为什么/如何工作。如果你有一个最喜欢的方法来使这个工作现在是时候展示它,请标记该代码,并张贴回来。

[编辑]

有人回答了,但是我还没来得及打勾,答案就被删除了。 在我的例子中,答案是一个模板函数。

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);
}
};
177854 次浏览

What you want to do is to make an interface which handles this code and all your classes implement the 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
{

Note that for this to work the "Callback" function is non static which i believe is an improvement. If you want it to be static, you need to do it as JaredC suggests with templates.

MyClass and YourClass could both be derived from SomeonesClass which has an abstract (virtual) Callback method. Your addHandler would accept objects of type SomeonesClass and MyClass and YourClass can override Callback to provide their specific implementation of callback behavior.

Instead of having static methods and passing around a pointer to the class instance, you could use functionality in the new C++11 standard: std::function and 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);
}
};

The addHandler method now accepts a std::function argument, and this "function object" have no return value and takes an integer as argument.

To bind it to a specific function, you use 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;
}

You need to use std::bind when adding the handler, as you explicitly needs to specify the otherwise implicit this pointer as an argument. If you have a free-standing function, you don't have to use std::bind:

void freeStandingCallback(int x)
{
// ...
}


int main()
{
// ...
handler->addHandler(freeStandingCallback);
}

Having the event handler use std::function objects, also makes it possible to use the new C++11 lambda functions:

handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });

If you have callbacks with different parameters you can use templates as follows:
// 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();
}

Here's a concise version that works with class method callbacks and with regular function callbacks. In this example, to show how parameters are handled, the callback function takes two parameters: bool and 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);

This restricts the C++11-specific code to the addCallback method and private data in class Caller. To me, at least, this minimizes the chance of making mistakes when implementing it.

Note that with C++20's bind_front you can simplify add_callback for class member functions to:

      template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int))
{
callbacks_.emplace_back(std::bind_front(mf, object));
}

A complete working example from the code above.... for 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

Output should be:

Compiler:201103


Handler added...
Result:6