【问题标题】:C++ callback using class member使用类成员的 C++ 回调
【发布时间】:2012-12-20 19:09:12
【问题描述】:

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

我知道了,它很简单,适用于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() 可以同时与MyClass 和YourClass 一起使用。很抱歉,但这只是我的大脑工作的方式,我需要先看一个简单的例子来说明什么是有效的,然后才能理解它为什么/如何工作。如果您有最喜欢的方式来完成这项工作,现在是展示它的时候了,请标记该代码并将其发回。

[编辑]

已回答,但在我打勾之前答案已被删除。 就我而言,答案是模板化函数。把 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);
        }
};

【问题讨论】:

  • 谁发布了模板化函数示例?你得到了复选标记,但在我测试时你删除了你的答案。它正是我需要的。一个简单的函数模板在我正在阅读的所有其他信息中迷失了。您的答案添加为问题的编辑。
  • 我认为是 JaredC。你可能需要追捕他=P

标签: c++ function callback member


【解决方案1】:

上面代码中的一个完整的工作示例......对于 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

输出应该是:

Compiler:201103

Handler added...
Result:6

【讨论】:

    【解决方案2】:

    这是一个简洁的版本,适用于类方法回调和常规函数回调。在这个例子中,为了展示如何处理参数,回调函数接受两个参数:bool 和 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);
    

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 您能解释一下您为解决 OP 的问题所做的工作吗?真的有必要包含OP的完整代码吗? OP 希望他的代码与他的YourClass 一起工作。您似乎已删除该类并添加了不同的OtherClass。而且,这个问题已经得到了很好的回答。您的解决方案在多大程度上更好,值得发布?
      • 我没有说我的帖子是一个更好的解决方案。我展示了如何以模板方式使用“OtherClass”。
      【解决方案4】:

      您可以使用新 C++11 标准中的功能:std::function 和 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);
              }
      };
      

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

      要将其绑定到特定函数,请使用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;
      }
      

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

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

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

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

      【讨论】:

      • 谢谢约阿希姆!这个例子在揭开 std::function 和 std::bind 的神秘面纱方面做了很多工作。我将来肯定会使用它! edit我仍然没有得到 lambda :)
      • 我把它折叠到我更大的项目中(大约 6,000 行。这对我来说很大。)它使用具有不同回调和参数的按钮定义向量,然后将其提供给 wxWidgets,因此对象可以管理自己的按钮在 wxFrame 中。这简化了很多事情!我不能说太多,互联网包含太多的技术性和观点,而没有足够的简单示例。
      • @user819640 没有“unbind”,std::bind 只是返回一个(未指定的)对象,当你完成它时,你可以让它超出范围。如果绑定的对象被破坏并且您尝试调用该函数,您将得到undefined behavior。
      • handler-&gt;addHandler(),表示在某处创建了EventHandler 的对象?顺便说一句,答案很好,+1。
      • 注意,占位符的数量需要与参数的数量相匹配,所以如果回调中有两个参数,则需要使用...., _1, _2)等等。
      【解决方案5】:

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

      【讨论】:

      • 对于我正在做的事情,我玩弄了这个想法。但由于使用我的处理程序的类的数量差异很大,我不认为它是一种选择。
      【解决方案6】:

      您要做的是创建一个处理此代码的接口,并且您的所有类都实现该接口。

      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
      {
      

      请注意,要使其正常工作,“回调”功能是非静态的,我相信这是一种改进。如果您希望它是静态的,则需要按照 JaredC 对模板的建议进行操作。

      【讨论】:

      • 你只展示了它的一侧。展示如何触发事件。
      猜你喜欢
      • 2011-08-23
      • 1970-01-01
      • 2010-11-03
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多