将功能指针存储到任何成员函数

时间:2012-10-20 12:14:41

标签: c++ function events function-pointers

我的活动经理

对于事件管理器,我需要在向量中存储许多指向函数的指针,以便在触发事件时调用它们。 (我将在本问题的最后提供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继承。)
  • 如何以其他方式设计我的活动管理器以达到目标功能? (我想使用string和int键来表示消息。)

我试图保持我的问题一般,但如果您需要更多信息或代码,请发表评论。

分配

在我的成员函数指针向量中,我使用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(); }
};

4 个答案:

答案 0 :(得分:5)

您必须使用std::function。这是实现泛型回调的唯一方法。一旦涉及函数指针而不是函数对象,它就不是通用的,永远不会是通用的,永远不会变成通用的。

unordered_map<string, vector<std::function<void()>>>

函数指针很糟糕,不应该在C ++中显式使用,只传递给像std::bindstd::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模式,您可能会在网上发现很多关于它的信息,以及许多失败的实现,但是谁知道你也可能会获得金牌

好的,首先问题有一个根本性的缺陷:它没有考虑到捕获对象引用是棘手的,因为对象的生命周期是有限的。

因此,即使在我们深入研究实现的细节之前,我们也需要问自己如何处理过时的引用。有两个基本策略:

  1. 没有过时的引用,这意味着注册的对象在销毁时自动取消注册。该机制可以在基类中进行考虑。

  2. 有一种方法可以在检查时分辨出好的和陈旧的参考文献,然后懒洋洋地收集陈旧的文章。可以使用shared_ptr / weak_ptr对强制执行该机制,并认识到weak_ptr的{​​{1}} 观察员

  3. 这两种解决方案都是可行的,实施都不是完美的。基类机制假设您可以实际修改类层次结构,而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 *会导致类型安全性丢失),所以我建议你寻找一种不同的方式来实现你想要的。