使用类成员的 C++ 回调

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14189440/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:06:27  来源:igfitidea点击:

C++ callback using class member

c++functioncallbackmember

提问by BentFX

I know this has been asked so many times, and because of that it's difficult to dig through the cruft and find a simple example of what works.

我知道这已经被问过很多次了,因此很难深入挖掘这些问题并找到一个简单的例子来说明什么是有效的。

I've got this, it's simple and it works for MyClass...

我有这个,它很简单,而且适用于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);
};

How can that be rewritten so EventHandler::addHandler()will work with both MyClassand YourClass. I'm sorry but it's just the way my brain works, I need to see a simple example of what works before I can comprehend why/how it works. If you've got a favorite way to make this work now's the time to show it off, please markup that code and post it back.

如何重写它,以便EventHandler::addHandler()同时使用MyClassYourClass。我很抱歉,但这只是我大脑的工作方式,我需要先看看一个简单的例子,然后才能理解它为什么/如何工作。如果您有最喜欢的方式来完成这项工作,现在是时候展示它了,请标记该代码并将其发回。

[edit]

[编辑]

It was answered but the answer was deleted before I could give the checkmark. The answer in my case was a templated function. Changed addHandler to this...

它得到了回答,但在我给出复选标记之前答案已被删除。就我而言,答案是模板化函数。将 addHandler 更改为此...

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);
        }
};

回答by Some programmer dude

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::functionand std::bind:

您可以使用新的 C++11 标准中的功能,而不是使用静态方法并传递指向类实例的指针:std::functionstd::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 addHandlermethod now accepts a std::functionargument, and this "function object" have no return value and takes an integer as argument.

addHandler方法现在接受一个std::function参数,这个“函数对象”没有返回值,并接受一个整数作为参数。

To bind it to a specific function, you use std::bind:

要将其绑定到特定函数,请使用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::bindwhen adding the handler, as you explicitly needs to specify the otherwise implicit thispointer as an argument. If you have a free-standing function, you don't have to use std::bind:

您需要std::bind在添加处理程序时使用,因为您需要明确指定其他隐式this指针作为参数。如果您有独立功能,则不必使用std::bind

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

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

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

让事件处理程序使用std::function对象,还可以使用新的 C++11 lambda 函数

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

回答by rsjaffe

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: booland int.

这是一个简洁的版本,适用于类方法回调和常规函数回调。在此示例中,为了显示如何处理参数,回调函数采用两个参数:boolint

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.

这将特定于 C++11 的代码限制为类 Caller 中的 addCallback 方法和私有数据。至少对我来说,这最大限度地减少了在实施时出错的机会。

回答by Karthik T

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 believeis an improvement. If you want it to be static, you need to do it as JaredC suggests with templates.

请注意,为此,“回调”功能是非静态的,我认为这是一种改进。如果你希望它是静态的,你需要按照 JaredC 建议的模板来做。

回答by s.bandara

MyClassand YourClasscould both be derived from SomeonesClasswhich has an abstract (virtual) Callbackmethod. Your addHandlerwould accept objects of type SomeonesClassand MyClassand YourClasscan override Callbackto provide their specific implementation of callback behavior.

MyClass并且YourClass都可以从中派生出SomeonesClass具有抽象(虚拟)Callback方法的方法。您addHandler将接受类型为SomeonesClassand 的对象,MyClass并且YourClass可以覆盖Callback以提供它们对回调行为的特定实现。

回答by Craig D

A complete working example from the code above.... for C++11:

来自上面代码的完整工作示例......对于 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  is your .cpp file name... this is the command used:
// g++ -std=c++11 -Wall -o  .cpp
// chmod 700 
// ./

Output should be:

输出应该是:

Compiler:201103

Handler added...
Result:6

回答by mohDady

If you have callbacks with different parameters you can use templates as follows:
// compile with: g++ -std=c++11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbacksApp

如果您有具有不同参数的回调,您可以使用如下模板:
// 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();
}