对于事件管理器,我需要在向量中存储许多指向函数的指针,以便在触发事件时调用它们。 (我将在本问题的最后提供EventFunction助手类的源代码。)
// an event is defined by a string name and a number
typedef pair<string, int> EventKey;
// EventFunction holds a pointer to a listener function with or without data parameter
typedef unordered_map<EventKey, vector<EventFunction>> ListEvent;
// stores all events and their listeners
ListEvent List;
注册侦听器可以通过调用第一个或第二个函数来完成,具体取决于您是否希望接收其他数据。 (此代码来自我的事件管理器类。)
public:
typedef void (*EventFunctionPointer)();
typedef void (*EventFunctionPointerData)(void* Data);
// let components register for events by functions with or without data parameter,
// internally simple create a EventFunction object and call the private function
void ManagerEvent::Listen(EventFunctionPointer Function, string Name, int State);
void ManagerEvent::Listen(EventFunctionPointerData Function, string Name, int State);
private:
void ManagerEvent::Listen(EventFunction Function, string Name, int State)
{
EventKey Key(Name, State);
List[Key].push_back(Function);
}
该代码不起作用,因为我存储函数指针但不存储我的List中的成员函数指针。所有这些指针应该是成员函数指针,因为像ComponentSound
这样的组件将使用其成员函数"PlayerLevelup"
监听事件ComponentSound::PlayerLevelup
,以便在触发事件时发出好听的声音。 / p>
C ++中的成员函数指针看起来像这样。
// ReturnType (Class::*MemberFunction)(Parameters);
void (ComponentSound::*PlayerLevelup)();
问题是,任何组件类都应该能够侦听事件,但是在事件管理器中存储成员函数指针需要我指定监听类。正如您在示例中所看到的,我需要指定ComponentSound
,但事件管理器应该只有一个指向任何类的成员函数指针的向量。
回答其中一个问题对我有很大的帮助。
Component
继承。)我试图保持我的问题一般,但如果您需要更多信息或代码,请发表评论。
在我的成员函数指针向量中,我使用EventFunction而不是仅提供两种消息类型的指针。一个有,有一个没有数据参数。
class EventFunction
{
private: EventFunctionPointer Pointer; EventFunctionPointerData PointerData; bool Data;
public:
EventFunction(EventFunctionPointer Pointer) : Pointer(Pointer), PointerData(NULL), Data(false) { }
EventFunction(EventFunctionPointerData PointerData) : PointerData(PointerData), Pointer(NULL), Data(true) { }
EventFunctionPointer GetFunction() { return Pointer; }
EventFunctionPointerData GetFunctionData() { return PointerData; } bool IsData() { return Data; }
void Call(void* Data = NULL){ if(this->Data) PointerData(Data); else Pointer(); }
};
答案 0 :(得分:5)
您必须使用std::function
。这是实现泛型回调的唯一方法。一旦涉及函数指针而不是函数对象,它就不是通用的,永远不会是通用的,永远不会变成通用的。
unordered_map<string, vector<std::function<void()>>>
函数指针很糟糕,不应该在C ++中显式使用,只传递给像std::bind
和std::function
的构造函数这样的模板,而成员函数指针则更糟。
答案 1 :(得分:4)
您可以使用仿函数来实现此目的。如果你在成员函数周围包含一个仿函数,你可以用仿函数创建一个向量。仿函数看起来像这样:
template <class T> class MyFunctor
{
private:
T* ObjectPtr;
void (T::*MemberFunction) ();
public:
void operator () ()
{
return (*this->ObjectPtr.*this->MemberFunction)();
}
};
所以基本上一个仿函数会覆盖()运算符并返回存储在仿函数类中的成员函数。如果你想让它们使用不同的签名,Functor可能会非常复杂,但在本文中你可以获得更多的信息。
http://www.codeproject.com/Articles/7112/Pointers-to-Member-Functions-and-Functors
答案 2 :(得分:3)
不是直接反应,所以忍受我。
在我们开始之前:这通常被称为Observer
模式,您可能会在网上发现很多关于它的信息,以及许多失败的实现,但是谁知道你也可能会获得金牌的
好的,首先问题有一个根本性的缺陷:它没有考虑到捕获对象引用是棘手的,因为对象的生命周期是有限的。
因此,即使在我们深入研究实现的细节之前,我们也需要问自己如何处理过时的引用。有两个基本策略:
没有过时的引用,这意味着注册的对象在销毁时自动取消注册。该机制可以在基类中进行考虑。
有一种方法可以在检查时分辨出好的和陈旧的参考文献,然后懒洋洋地收集陈旧的文章。可以使用shared_ptr
/ weak_ptr
对强制执行该机制,并认识到weak_ptr
的{{1}} 观察员。
这两种解决方案都是可行的,实施都不是完美的。基类机制假设您可以实际修改类层次结构,而shared_ptr
技巧假定所有观察将被堆分配,其生命周期由weak_ptr
控制。
我将使用weak_ptr
做一个例子(并使用许多C ++ 11工具,但这里没有必需):
shared_ptr
答案 3 :(得分:2)
这是典型的情况,你应该使用多态而不是函数(或成员函数)指针。
如您所述,您的组件类应继承自公共类Component,其中包含表示事件的虚拟方法:
class Component
{
public:
virtual void OnPlayerLevelUp()
{
}
};
class ComponentSound : public Component
{
public:
// override it
void OnPlayerLevelUp()
{
// do the actual work
}
};
您的ListEvent类型现在将如下所示:
typedef unordered_map<EventKey, vector<Component*>> ListEvent;
对于事件方法中的可选void * paramenter,您可以将其指定为可选参数,但事实上它是一个空格*是一个坏符号(使用void *会导致类型安全性丢失),所以我建议你寻找一种不同的方式来实现你想要的。