多态成员函数指针

时间:2009-06-25 23:35:14

标签: c++ function-pointers

我正在尝试在DirectX9中编写回调事件系统。我正在尝试使用方法函数指针来触发鼠标点击事件;但是我遇到了一些问题。我的游戏使用游戏状态管理器来管理渲染。我的所有游戏状态都来自基类AbstractGameState。

我有一个使用这种特定方法的精灵对象:

m_NewGameSprite->OnClick(this, &MainMenuState::StartGame);

MainMenuState是我游戏所在的当前游戏状态,而StartGame是此类的void方法部分。我想将函数指针存储在我的精灵类中的变量中,这样我就可以在用户点击时执行它。

template <typename T>
void OnClick(GameState* LinkedState, void (T::*EventPointer)())
{
    m_LinkedGameState = LinkedState;
    m_EventPointer = EventPointer; // <- Doesnt compile
}

我尝试了向下转换指针,但这确实不起作用。

我的精灵类也包含这两个变量

void                (GameState::*m_EventPointer)();
GameState*          m_LinkedGameState;

任何帮助将不胜感激

5 个答案:

答案 0 :(得分:2)

我真的不知道为什么你的任务不起作用,毫无疑问,litb将很快解释原因。 Boost.Function是一个漂亮的,通用的,类型安全的标准函数对象,可以在几乎所有情况下用作函数指针的替代。我会这样做:

typedef boost::function0<void> Event;
Event m_Event;

请注意,事件类现在封装了函数调用的状态,包括要调用它的对象。通常你也使用Boost.Bind来创建一个闭包,但你也可以很容易地绑定到一个自由函数或一些其他函数对象。

void OnClick(Event &event)
{
  m_Event=event;
}

这样称呼:

OnClick(boost::bind(&MainMenuState::StartGame, this));

使用此方案,您实际上不需要存储“链接游戏状态” - 这是封装在事件对象中。

答案 1 :(得分:1)

此处的问题是StartGame无法使用GameState参数调用任何this实例。只能使用类型为this的{​​{1}}参数调用它。

MainMenuState指向void (GameState::*)()中定义的方法,该方法必须是MainMenuState中定义的虚方法。

我建议不要试图存储成员函数指针,而是使用以下内容存储“命令对象”(或函子)指针:

GameState

然后定义这样的实现:

class Callback
{
public:
    virtual void Execute() = 0;
};

然后,您可以定义template <typename TClass> class MemberCallback : public Callback { public: MemberCallBack(TClass * pThis, void (TClass::*pFunc)() ) { m_pThis = pThis; m_pFunc = pFunc; } void Execute() { m_pThis->*m_pFunc(); } private: TClass * m_pThis; void (TClass::*m_pFunc)(); }; 类型的成员。

答案 2 :(得分:1)

我有一个非常类似的问题。如果我正确地读到这个,你试图在Java中编写一个类似于ActionEvent的系统。这看起来像这样:

Component.addActionListener( new ActionEventListener( new ActionEvent(
            public void execute() { /* Component clicked Do Something */ } )));

在C ++中,有一个非常类似的事情要做,如下所示。

在我看来,这要好得多,因为你可以实际重用方法调用并随意修改它们,这会给你更大的灵活性。

Event.hpp

#pragma once

class Event 
{
    public:
        Event(void) { }
        ~Event(void) { }

    public:
        //Stores a function to callback. Example shown in Main.cpp
        void operator += (void (*callback)())
        {
            this->callback = callback;
        }

        //Executes our stored call back method at any given time.
        void execute(void)
        {
            callback();
        }

        //Variable of type VOID* -> This is a function which is stored + executed.
        private:
            void (*callback)();
};

Main.cpp的

#include <iostream>
#include "Event.hpp"

using namespace std;

void print(void)
{
    cout << "Hello This Works" << endl;
}

void print2(void)
{
    cout << "Success again!" << endl;
}

int main()
{
    Event task_event;
    task_event += print;
    task_event.execute();
    task_event += print2;
    task_event.execute();
    system("pause");
    return 0;
}

答案 3 :(得分:0)

为什么你在那里使用模板功能?对于GameState以外的OnClickT将无法编译或工作。

但是,在这种情况下,我通常不会使用指向成员函数的指针。我创建了一个函数 object ,重载operator(),然后传递它们。这样更好:语法更清晰,如果你发现需要,你可以在函数对象中存储一些状态。

答案 4 :(得分:0)

您应该将参数作为:

void (GameState::*EventPointer)()

因为一旦它是一个指针,就不能像那样向下转发(它没有关于基类中是否存在相同指针的信息)。这些函数必须在GameState上才能工作(可能是虚拟的)

然而!由于您正在进行回调(基本上是观察者模式),因此您可能需要仔细查看boost::signals。它将完成所有这些(以及更多)。您无需为GameState添加功能。

class Foo {
 // ...
 template <typename T>
 boost::signals::connection OnClick(const boost::function<void ()>& func)
 {
    return sig.connect(func);
 }

 void FireClick() { sig_(); }
private:
  boost::signal<void ()> sig_;
};

int this_will_be_called() { }

Foo f = // ...;

f.OnClick(boost::bind(&this_will_be_called));