如何添加/设计回调函数

时间:2010-04-27 13:10:03

标签: c++ callback queue

如果要从队列中读取数据,如何在C ++中设置/注册回调函数来调用函数?

修改1:

使用Neil的答案获得完整答案(在头文件中):

#include <vector.h>

class QueueListener {
   public:
       virtual void DataReady(class MyQueue *q) = 0;
       virtual ~QueueListener() {}
};

class MyQueue {
   public:
       void Add (int x) {
          theQueue.push_back(x);
          for (int i = 0; i < theCallBacks.size(); i++) {
             theCallBacks[i]->DataReady(this);
          }
       }

       void Register (QueueListener *ql) {
            theCallBacks.push_back(ql);
       }


   private:
       vector <QueueListener *> theCallBacks;
       vector <int> theQueue;
};



class MyListener : public QueueListener {
   public:
       virtual ~MyListener () {
          printf("MyListener destructor!");
       }
       MyListener(MyQueue *q);
       virtual void DataReady(class MyQueue *p);
};

注册:

#include "File1.h"


MyListener::MyListener(MyQueue *q)
{
   q->Register(this);
}

void MyListener::DataReady(class MyQueue *p)
{
   Sleep(500);
}

然后是电话:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    MyQueue *q = new MyQueue();
    MyListener ml(q);

    q->Add(1);

}

3 个答案:

答案 0 :(得分:3)

概括地说,创建一个QueueListener基类:

class QueueListener {
   public:
       virtual void DataReady( class MyQueue & q ) = 0;
       virtual ~QueueListener() {}
};

和一个队列类(以整数队列为例:

class MyQueue {

   public:
      void Add( int x ) {
          theQueue.push_back( x );
          for ( int i = 0; i < theCallBacks.size(); i++ ) {
              theCallBacks[i]->DataReady( * this );
          }
      }

      void Register( QueueListener * ql ) {
          theCallBacks.push_back( ql );
      }

  private:

    vector <QueueListener *> theCallBacks;
    SomeQueueType <int> theQueue;

};

您派生了想要从QueueListener回调的类并实现DataReady函数。然后,使用队列实例注册派生类的实例。

答案 1 :(得分:3)

看看Boost.Signals。

从教程中窃取的示例:

struct HelloWorld 
{
  void operator()() const 
  { 
    std::cout << "Hello, World!" << std::endl;
  } 
};

// ...

// Signal with no arguments and a void return value
boost::signal<void ()> sig;

// Connect a HelloWorld slot
HelloWorld hello;
sig.connect(hello);

// Call all of the slots
sig();

答案 2 :(得分:2)

我喜欢boost.asio用于回调的方法。在ASIO中,它们被称为处理程序。请原谅我的c ++ 0x,它写得比c ++ 98快得多。

class MyQueue
{
   //...
   Register( const std::function<void()>& callback )
   {
      m_callbacks.push_back(callback);
   }

   Add( const int& i )
   {
      // ...

      for( const auto& callback: m_callbacks )
      {
         callback();
      }
   }

   std::vector<std::function<void()>> m_callbacks;
};

class SomeClass
{
public:
   void SomeQueueIsReady( MyQueue& )
   { /* do something with MyQueue */ }
};

void register_callback()
{
   SomeClass some;
   MyQueue queue;

   // using bind
   queue.Register( std::bind( &SomeClass::SomeQueueIsReady, &some, std::ref(queue) ) );

   // or using a lambda
   queue.Register( [&queue,&some](){ some.SomeQueueIsReady( queue ); } );
}

关键点是回调是一个仿函数,因此用户不依赖于特定的类层次结构,并且回调不接受任何参数。如果您想要传入参数,请自行绑定它们。例外情况是,回调在注册回调时产生的信息不可用。一个例子可能是添加项目的时间。

没有什么能阻止你在c ++ 98中使用这个解决方案。你不能使用lamdbas,但是boost::functionboost::bind与它们的c ++ 0x计数器部分几乎相同。

请注意,您必须仔细管理对象的生命周期。无论是Neil还是我的解决方案都是如此。