C++11中的std::function可以接受函数指针、std::bind、lambda表达式等函数,可以达到很松的耦合,简直就是为事件机制设计的。但是不能简单判断两个std::function是不是同一函数,所以下面我用ID来标识listener
我模仿C#的委托实现了观察者模式:
#pragma once
#include <functional>
#include <map>
#include <crtdbg.h>
template<class KeyCmp, class... ArgTypes>
class Event final
{
public:
typedef std::function<void(ArgTypes...)> FunctionType;
private:
class Listener
{
public:
FunctionType m_function;
// 这里可以添加额外信息
};
std::map<int, Listener, KeyCmp> m_listeners;
int m_nextListenerID = 0;
public:
// 返回listener ID
int AddListener(FunctionType callback)
{
int listenerID = m_nextListenerID++;
Listener& listener = m_listeners[listenerID];
listener.m_function = std::move(callback);
return listenerID;
}
void DeleteListener(int listenerID)
{
m_listeners.erase(listenerID);
}
void operator () (ArgTypes... args)
{
for (auto it = m_listeners.cbegin(); it != m_listeners.cend(); ++it)
{
try
{
it->second.m_function(std::forward<ArgTypes>(args)...);
}
catch (std::bad_function_call&)
{
_RPTF0(_CRT_WARN, "回调事件时出现bad_function_call\n");
}
if (m_listeners._Isnil(it._Mynode()))
{
// 不能在回调时把这个listener删了
_RPTF0(_CRT_WARN, "迭代调用事件时当前结点被删除\n");
break;
}
}
}
};
// 先监听先被调用
template<class... ArgTypes>
using PreEvent = Event<std::less<int>, ArgTypes...>;
// 先监听后被调用
template<class... ArgTypes>
using PostEvent = Event<std::greater<int>, ArgTypes...>;
使用方法:
PreEvent<int, const std::string&> g_someEvent;
int _tmain(int argc, _TCHAR* argv[])
{
// 添加监听器
int id = g_someEvent.AddListener([](int arg1, const std::string& arg2){
std::cout << "arg1 = " << arg1 << std::endl;
std::cout << "arg2 = " << arg2 << std::endl;
});
// 触发事件
g_someEvent(123, "test");
// 删除监听器
g_someEvent.DeleteListener(id);
return 0;
}