2
typedef void (FunctionSet::* Function)();

class MyFunctionSet : public FunctionSet
{
protected:
    void addFunctions()
    {
        addFunction(Function(&MyFunctionSet::function1));
    }

    void function1()
    {
        // Do something.
    }
};

addFunction メソッドは関数を基本クラスのリストに追加します
。これを列挙してすべての関数を呼び出すことができます。

関数の追加を簡素化する (タイピング作業を減らす) 方法はありますか?

4

5 に答える 5

2

これを行うことで何を達成しようとしているのか説明できますか?

これはかなり悪い設計のように思えます。純粋な仮想関数 1 を持つ "Computable" などの抽象クラス ("c++ インターフェイス") を作成し、実装ごとに Computable をサブクラス化し、MyFunctionSet に Computable のセットを維持させることはできませんか

関数ポインタを使用している特定の理由はありますか?

于 2009-01-19T23:52:01.133 に答える
2

だから、私はウリに反対する傾向があります。

オブザーバー パターンを実装している場合は、次のように言えます。

「オブジェクトXがYを実行するとき、コードZを実行したい」.

純粋に抽象クラスに基づくアプローチを採用することは、最善の方法ではありません。すべてのイベント ハンドラーに個別のクラス (特に C++) を要求するのはやり過ぎです。あなたがJavaから来ているなら、それは彼らがすべてを行う方法です. ただし、Java には、匿名クラスと「インスタンス」メンバー クラスという 2 つの機能があります。これにより、同じクラス内の複数のイベントに対して複数のハンドラーを定義できます。メソッドの前に「class {」を付ける必要がありますが、それは可能です。

C++ には、無名クラスも「インスタンス」メンバー クラスもありません。これにより、状態を共有する必要がある複数のハンドラーがある場合、イベントに抽象クラスを使用するのが非常に面倒になります。クロージャの生成に相当することを手動で行う必要があり、これは非常にすぐにイライラする可能性があります。

.NET はイベント処理にデリゲートを使用しますが、これは基本的にタイプ セーフな関数ポインターです。これらは、イベントを処理するためのコードを非常に簡単にします。ただし、C++ の関数ポインターとは異なり、デリゲートはメンバー関数ポインターと静的関数ポインターを統合します。基本的に、静的関数ポインターのように見える関数ポインターを残して、任意のメンバー関数の「this」パラメーターをカリー化できます。これは、イベントを処理するオブジェクトのタイプがイベント「インターフェース」の一部ではないことを意味します。それはそれを非常に柔軟にします。

「this」型は最終的に関数ポインター型の一部になるため、C++ メンバー関数ポインターを直接使用してこれを行うことはできません。したがって、ハンドラーは現在のクラス内にのみ表示されるように制限されます。

C++ の最適なソリューションは、2 つのハイブリッドです。必要なのは、次のような汎用インターフェイスです。

class EventHandler
{
public:
    virtual void Handle() = 0;
};

そして、このようなメンバー関数の実装

template <class T>
class MemberFuncEventHandler : public EventHandler
{
public:


    MemberFuncEventHandler(T * pThis, void (T::*pFunc)()) : m_pThis(pThis), m_pFunc(pFunc)
    {
    }

    void Handle()
    {
        (m_pThis->*m_pFunc)();
    } 
private:
    T* m_pThis;
    void (T::*m_pFunc)();
};

template <class T>
EventHandler * Handler(T * pThis, void (T::*pFunc)())
{
    return new MemberFuncEventHandler<T>(pThis, pFunc);
}

同様に、静的メソッドのハンドラー クラスを定義することもできます。次に、次のようなことができます。

Handlers += Handler(obj, &(c1::foo));
Handlers += Handler(obj, &(c2::bar));
Handlers += Handler(blaa); // for static methods...
于 2009-01-20T00:53:05.517 に答える
2

派生クラスの関数へのメンバー関数ポインターを、基本クラスの関数へのメンバー関数ポインターに割り当てているようです。型システムに穴を開けるので、それは禁じられています。それは驚きです(少なくとも私にとっては、初めて聞いたときです)。理由については、この回答をお読みください。

実際の質問に答えるにはaddFunction、テンプレートを作成します:

void addFunctions() {
    addFunction(&MyFunctionSet::function1);
}

addFunctionベースクラスを次のように変更します。

template<typename Derived>
void addFunction(void(Derived::*f)()) {
    myFunctions.push_back(static_cast<Function>(f));
}

が実際に から派生していないstatic_castかどうかがわかるため、 を使用したほうがよいでしょう。DerivedFunctionSet

于 2009-01-20T00:24:06.840 に答える
1

加算演算子をオーバーロードできると思います。

于 2009-01-19T23:48:55.023 に答える
1

コールバック システムなどを作成しようとしている場合は、Boost.Signals ライブラリをお勧めします。基本的に、関数を集約し、(他の信号ライブラリと同様に) コマンドで関数グループを呼び出しますが、素晴らしい Boost.Bind と Boost.Function で動作するようにも設計されています。

例:

using boost::function
using boost::bind
using boost::signal

void some_other_func();    

Foo a;
Bar b;

signal<void()> sig;

sig.connect(bind(&Foo::foo,&a));
sig.connect(bind(&Bar::bar,&b));
sig.connect(some_other_func);

sig(); // calls -> a.foo(), b.bar(), some_other_func()

ブロッキング、洗練された接続管理、その他の優れた機能もサポートしています。

于 2009-01-20T01:04:39.863 に答える