Мне нравится подход, который 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::function
и boost::bind
почти идентичны их счетным частям c ++ 0x.
Помните, что вам придется тщательно управлять временем жизни объекта. Так обстоит дело с Нейлом или моим решением.