2

クラス関数に別のクラス不明の関数を使用させようとしています。それが理にかなっていることを願っています。私がやろうとしているのは...button1(または宣言されたボタン)がクリックされると、関数Event__MouseClickedの最初のパラメーターに入力された関数(「void関数」)が実行されます。

bool MouseClicked( void ){ /**/ };

namespace XE
{
    class Window
    {
    public:
        const char* Title;
    };

    class Button
    {
    public:
        void Event__MouseClicked( void( *Function )( void ) ) {
            if( MouseClicked( ) )
                ( *Function )( );
        }
    };
};

class XorrWindow1 : public XE::Window
{
public:
    XorrWindow1( void ) {
        Initialize( );
    }
protected:
    ~XorrWindow1( void );
private:
    XE::Button Button1;
    XE::Button Button2;
private:
    // EVENT PROTOTYPES
    void DoSomething( void );
    void RandomFunction( void );

    void Initialize( void )
    {
        // INITIALIZE CONTROLS
        // AND OTHER STUFF BELOW
        this->Title = "XorrWindow1";

        // How can I resolve this problem?
        this->Button1.Event__MouseClicked( &XorrWindow1::DoSomething );
        this->Button2.Event__MouseClicked( &XorrWindow1::RandomFunction );
    };
};

void XorrWindow1::DoSomething( void ) {
    ::MessageBoxA( NULL, this->Title, "Button1 clicked!", MB_OK );
};

void XorrWindow1::RandomFunction( void ) {
    ::MessageBoxA( NULL, this->Title, "Button2 clicked!", MB_OK );
};

エラーは次のとおりです。

'XE::Button::Event__MouseClicked' : cannot convert parameter 1 from 'void (__thiscall XorrWindow1::* )(void)' to 'void (__cdecl *)(void)'

エラーの原因は完全に理解しています。しかし、それはクラスWindow1の未知の関数をとることができなければならないので、それを修正する方法がわかりません。

4

3 に答える 3

3

Karel Petranekが言ったように、すべてのケースに対応するためのテンプレートが必要です。最も一般的な方法は次のとおりです(どこでもSTLで利用されています)。

template<typename f>
void Event__MouseClicked(f&& func)
{
    if( MouseClicked( ) )
        func();
}

基本的Event__MouseClickedに、呼び出し可能なオブジェクトを受け入れます。あなたの場合、あなたはそれをマルセロ・カントスが説明したように呼ぶでしょう:

Button1.Event__MouseClicked([&]{ DoSomething(); });

ただし、このメソッドの利点は、引数をとらない呼び出し可能オブジェクトを渡すEvent__MouseClickedと、コンパイルされて機能することです。関数ポインタ、結果std::bind、a std::function、ラムダなどを渡すことができます。

std::functionこれは、たとえばストレート関数ポインタを受け入れる場合など、場合によってはaを強制的に受け入れるよりも最適です。

于 2013-01-20T21:49:55.790 に答える
2

必要に応じてキャプチャして渡す方法がないため、メンバー関数ポインタをフリー関数ポインタとして渡すことはできませんthis

この問題には多くのさまざまな解決策があります。C ++ 11を使用している場合、最もエレガントなのはstd::function<void()>:を渡すことです。

virtual void Event__MouseClicked(const std::function<void()>& f) {
    if (MouseClicked()) f();
}
⋮
Button1.Event__MouseClicked([&]{ DoSomething(); });

編集:ラムダを使用すると、コールバックメンバー関数を廃止することもできることをほとんど忘れていました:

Button1.Event__MouseClicked([&]{
    ::MessageBoxA( NULL, this->Title, "Button1 clicked!", MB_OK );
});

これはテンプレート関数ではないため、派生クラスでオーバーライドできます。ただし、GUIフレームワークでは通常Button、動作をカスタマイズするためにクラスから派生する必要はなく、イベントの一部としてコールバックを渡さないことに注意してください。一般的なパターンは、ボタンがコールバック関数を保持し、必要に応じて呼び出すことです。例えば:

class Button {
⋮
public:
    void onClicked(std::function<void(Button*)>& f) { clicked_ = f; }
    void click() { clicked_(this); }
private:
    std::function<void(Button*)> clicked_;

    // Internal click handler responds to OS click events.
    void handleClickEvent(…) { clicked_(this); }
};

上記は世界最高のデザインではありませんが、私が話していることを理解できるはずです。

于 2013-01-20T21:44:26.470 に答える
1

考えられるすべてのクラスに対応するには、テンプレートを使用する必要があります。

        template <typename T>
        void Event__MouseClicked(T *object, void( T::*Function )( void ) ) {
            if( MouseClicked( ) )
                ( object->*Function )( );
        }

使用する:

this->Button2.Event__MouseClicked( this, &XorrWindow1::RandomFunction );

ただし、テンプレート化された関数は仮想化できないため、サブクラスでこのメソッドをオーバーライドする機能が失われることに注意してください。また、メソッドポインタと一緒に「this」ポインタを渡す必要があります。

于 2013-01-20T21:39:47.247 に答える