4

別のクラス オブジェクトのベクトルをメンバーとして持つクラスがあります。このクラスの多くの関数では、ベクター内のすべてのオブジェクトに対して同じ操作を行う必要があります。

class Small
{
  public:
    void foo(); 
    void bar(int x);
    // and many more functions
};

class Big
{
  public:
    void foo()
    {
        for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
            VectorOfSmalls[i]->foo();
    }
    void bar(int x)
    {
        for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
            VectorOfSmalls[i]->bar(x);
    }
    // and many more functions
  private:
    vector<Small*> VectorOfSmalls;
};

私はコードを単純化し、すべての関数で他のベクトルを複製しない方法を見つけたいと考えています。

関数へのポインターを受け取り、ベクトルのすべてのメンバーで指定された関数を呼び出す関数を作成することを検討しました。しかし、C++ で関数へのポインターを使用することが良い考えかどうかはわかりません。

functor とfunctionoidについても考えていますが、関数ごとにクラスを作成する必要があり、やり過ぎのように思えます。

別の可能な解決策は、文字列を受け取り、文字列に従ってコマンドを呼び出す関数を作成することです。

void Big::call_command(const string & command)
{
    for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
    {
       if (command == "foo")
           VectorOfSmalls[i]->foo();
       else if (command == "bar")
           VectorOfSmalls[i]->bar();
    }
}
void Big::foo()
{
    call_command("foo");
}

ただし、動作が遅くなる可能性があり (単なる関数呼び出しではなく不要な文字列が作成される)、関数のシグネチャが異なる場合にも問題が発生します。

それで、あなたは何をお勧めしますか?今のままでいいのでしょうか?

編集:ブースト(古いコンパイラ)ではなく、STLのみを使用できます。

4

3 に答える 3

16

for ループを書き直して、イテレータやその他の STL を次のように使用できます。

void foo() {
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo));
}

void bar() {
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar));
}

それを超えて、いくつかのマクロを使用して、多くの再入力を避けることができますが、私はそれの大ファンではありません. 個人的には、コマンド文字列を取る単一の関数よりも複数の関数が好きです。決定がどのように行われるかについて、より多くの汎用性が得られるからです。

どちらを行うかを決定するためにパラメーターを取る単一の関数を使用する場合、列挙型とこのようなスイッチを使用します。これは、文字列とカスケード if よりも効率的です。また、あなたの例では、ループ内で何をするかを決定する if があります。呼び出しごとに「どのコマンド」を決定するだけでよいため、ループの外側をチェックしてループの冗長コピーを作成する方が効率的です。(注: コンパイル時にわかっている場合は、コマンドをテンプレート パラメーターにすることができます。そのように聞こえます)。

class Big {
public:
    enum Command {
        DO_FOO,
        DO_BAR
    };

void doit(Command cmd) {
    switch(cmd) {
    case DO_FOO:
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo));
        break;
    case DO_BAR:
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar));
        break;
    }
};

また、おっしゃったように、 &Small::whatever というメンバ関数ポインタを置き換えて、それをパラメータとして渡すだけでも簡単です。テンプレートにすることもできます。

class Big {
public:
    template<void (Small::*fn)()>
    void doit() {
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn));
    }
};

次に、次のことができます。

Big b;
b.doit<&Small::foo>();
b.doit<&Small::bar>();

このメソッドと通常のパラメーター メソッドの両方の優れた点は、ルーチンを増やすために small を変更しても、Big を変更する必要がないことです。これが好ましい方法だと思います。

単一のパラメーターを処理できるようにする場合は、bind2nd も追加する必要があります。完全な例を次に示します。

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

class Small {
public:
    void foo() { std::cout << "foo" << std::endl; }
    void bar(int x) { std::cout << "bar" << std::endl; }
};


class Big {
public:
    template<void (Small::*fn)()>
    void doit() {
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn));
    }

    template<class T, void (Small::*fn)(T)>
    void doit(T x) {
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::bind2nd(std::mem_fun(fn), x));
    }
public:
    std::vector<Small *> VectorOfSmalls;
};

int main() {
    Big b;
    b.VectorOfSmalls.push_back(new Small);
    b.VectorOfSmalls.push_back(new Small);

    b.doit<&Small::foo>();
    b.doit<int, &Small::bar>(5);
}
于 2009-01-19T18:25:03.943 に答える
4

std ライブラリを使用している場合は、for_eachを確認する必要があります。

C++ で関数ポインターを使用するのは良い考えではないかもしれないと述べていますが、心配する前に、これがパフォーマンスのボトルネック領域であるかどうかを確認する必要があります。

于 2009-01-19T18:03:19.637 に答える
0

boost::functionboost::bindを試してください:

void Big::call_command(const boost::function<void (Small*)>& f)
{
    for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
    {
        f(VectorOfSmalls[i]);
    }
}

int main()
{
    Big b;
    b.call_command(boost::bind(&Small::foo, _1));
    b.call_command(boost::bind(&Small::bar, _1, 5));
}
于 2009-01-19T18:28:37.947 に答える