سؤال

أحاول كتابة نظام حدث رد الاتصال في DirectX9.أحاول استخدام مؤشرات دالة الطريقة لتشغيل الأحداث من خلال نقرات الماوس؛ولكن لدي بعض المشاكل.تستخدم لعبتي مدير gamestate لإدارة العرض.جميع حالات الألعاب الخاصة بي مستمدة من فئة أساسية AbstractGameState.

لدي كائن كائن بهذه الطريقة المحددة:

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

MainMenuState هي حالة اللعبة الحالية التي توجد بها لعبتي، وStartGame جزء من الأسلوب الفارغ في هذه الفئة.أرغب في تخزين مؤشر الوظيفة في متغير داخل فئة الكائنات الخاصة بي حتى أتمكن من تنفيذها عندما ينقر المستخدم.

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;

سيكون موضع تقدير أي مساعدة

هل كانت مفيدة؟

المحلول

وأنا لا أعرف حقا لماذا لا يعمل مهمتك، ولا شك litb سوف يكون على طول قريبا لشرح لماذا. Boost.Function هو جميل، عام، typesafe وجوه وظيفة القياسية التي يمكن استخدامها كبديل للمؤشرات الدالة في ظروف كلها تقريبا. وأود أن تفعل هذا:

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

لاحظ أن فئة الحدث بتغليف الآن حالة استدعاء دالة، بما في ذلك الكائن الذي تريد الاتصال عليه. وعادة ما تستخدم أيضا Boost.Bind لإنشاء الإغلاق، ولكن يمكنك ربط بسهولة إلى وظيفة خالية أو بعض غيرها من وجوه وظيفة أيضا.

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

ويطلق عليه مثل هذا:

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

مع هذا المخطط، أنت لا تحتاج إلى تخزين "دولة عبة المرتبط" - وهذا يتم تغليف في كائن الحدث

.

نصائح أخرى

كان لدي مشكلة مشابهة جدا.إذا كنت أقرأ هذا بشكل صحيح فإنك تحاول كتابة نظام يشبه تقريبًا نظام ActionEvent في جافا.والتي يمكن أن تبدو مثل هذا:

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

في C++، هناك شيء مشابه جدًا يجب القيام به كما هو موضح أدناه.

في رأيي، هذا أفضل بكثير لأنه يمكنك بالفعل إعادة استخدام استدعاءات الطريقة وتعديلها حسب الرغبة، مما يمنحك المزيد من المرونة.

حدث.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;
}

والمشكلة هنا هي أن لا يمكن أن تسمى مع أي مثيل StartGame باستخدام المعلمة GameState لها this. لا يمكن إلا من خلال دعا مع معلمة this من نوع MainMenuState.

لديك نقطة void (GameState::*)() إلى طريقة محددة في MainMenuState، يجب أن يكون الأسلوب أسلوب ظاهري التي تم تعريفها أيضا في GameState.

وأود أن أوصي بأن بدلا من محاولة لتخزين مؤشر الدالة العضو، تخزين "كائن الأمر" (أو functor) المؤشر، وذلك باستخدام شيء من هذا القبيل:

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)();
};

وبعد ذلك يمكنك تحديد عضو من النوع Callback *.

لماذا أنت باستخدام دالة قالب هناك؟ سوف OnClick لا ترجمة، أو العمل، لقيم T أخرى من GameState.

ولكن، في هذه الحالة عموما أنا لن تستخدم مؤشر إلى وظيفة عضو. فما استقاموا لكم فاستقيموا إنشاء دالة <م> الكائن ، والحمولة الزائدة operator()، وتمرير المحيطين بدلا من ذلك. هذا هو أفضل: بناء الجملة أكثر وضوحا، ويمكنك تخزين بعض الدول في كائن وظيفة، يجب أن تجد أنك بحاجة إلى

ويجب أن تأخذ المعلمة بأنه:

void (GameState::*EventPointer)()

ولأن بمجرد أن يكون مؤشر، فإنه لا يمكن مسبل من هذا القبيل (انه ليس لديها معلومات عن حالة وجود نفس المؤشر في الفئة الأساسية). سوف الوظائف يجب أن تكون على GameState لهذا العمل (يحتمل أن تكون افتراضية)

ولكن! منذ تقومون به الاسترجاعات (أساسا نمط مراقب) قد نلقي نظرة طويلة على دفعة :: إشارات . وسوف تفعل كل هذا (وأكثر). لا ترتبط إلى الحاجة إلى إضافة وظائف إلى 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));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top