迭代向量和调用函数

时间:2022-04-26 07:41:35

I have a class that has a vector of another class objects as a member. In many functions of this class I have to do same operation on all the objects in the vector:

我有一个类,其中包含另一个类对象的向量作为成员。在这个类的许多函数中,我必须对向量中的所有对象执行相同的操作:

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

I want to simplify the code, and find a way not to duplicate going other the vector in every function.

我想简化代码,并找到一种方法,不要在每个函数中复制其他向量。

I've considered creating a function that receives a pointer to function, and calls the pointed function on every member of a vector. But I am not sure that using pointers to functions in C++ is a good idea.

我考虑过创建一个接收函数指针的函数,并在向量的每个成员上调用指向函数。但我不确定在C ++中使用函数指针是个好主意。

I have also been thinking about functors and functionoids, but it will force me to create a class per each function and it sounds like an overkill.

我也一直在考虑函子和函数,但是它会强迫我为每个函数创建一个类,这听起来像是一种矫枉过正。

Another possible solution is creating a function that receives a string, and calls the command according to the string:

另一种可能的解决方案是创建一个接收字符串的函数,并根据字符串调用该命令:

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

But it might work slow (unneeded creation of a string instead of just a function call), and also creates a problem if functions have different signature.

但它可能工作缓慢(不需要创建字符串而不仅仅是函数调用),并且如果函数具有不同的签名也会产生问题。

So what would you recommend? Should I leave everything the same as it is now?

那么你会推荐什么?我应该保留现在的一切吗?

EDIT: I can use only STL and not boost (old compilers).

编辑:我只能使用STL而不是boost(旧编译器)。

3 个解决方案

#1


16  

Well you can rewrite the for loops to use iterators and more of the STL like this:

那么你可以重写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));
}

beyond that, you could use some macros to avoid retyping that a lot, but I'm not a huge fan of that. Personally, I like the multiple functions over the single one which takes a command string. As it gives you more versatility over how the decision is made.

除此之外,你可以使用一些宏来避免重新输入那么多,但我并不是那种*的粉丝。就个人而言,我喜欢使用命令字符串的单个函数。因为它为您提供了更多的决策权。

If you do go with a single function taking a param to decide which to do, I would use an enum and a switch like this, it would be more efficient than strings and a cascading if. Also, in your example you have the if to decide which to do inside the loop. It is more efficient to check outside the loop and have redundant copies of the loop since "which command" only needs to be decided once per call. (NOTE: you can make the command a template parameter if it is known at compile time, which it sounds like it is).

如果你选择使用一个函数来决定做什么,我会使用枚举和这样的开关,它会比字符串和级联更有效。此外,在您的示例中,您有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;
    }
};

Also, as you mentioned, it is fairly trivial to replace the &Small::whatever, what a member function pointer and just pass that as a parameter. You can even make it a template too.

另外,正如你所提到的,替换&Small :: whatever,成员函数指针并将其作为参数传递是相当简单的。你甚至可以把它变成一个模板。

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

Then you can do:

然后你可以这样做:

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

The nice thing about both this and the regular parameter methods is that Big doesn't need to be altered if you change small to have more routines! I think this is the preferred method.

关于这个和常规参数方法的好处是,如果你改变小以拥有更多例程,则不需要改变Big!我认为这是首选的方法。

If you want to be able to handle a single parameter, you'll need to add a bind2nd too, here's a complete example:

如果你想要能够处理单个参数,你还需要添加一个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);
}

#2


4  

If you're using the std library, you should take a look at for_each.

如果你正在使用std库,你应该看看for_each。

You mention that using function pointers in C++ might not be a good idea, but -- allowing your worry is speed -- you have to see if this is even a performance bottleneck area you're in, before worrying.

你提到在C ++中使用函数指针可能不是一个好主意,但是 - 让你担心的是速度 - 在担心之前你必须看看这是否是你所处的性能瓶颈区域。

#3


0  

Try boost::function and boost::bind:

尝试boost :: function和boost :: 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));
}

#1


16  

Well you can rewrite the for loops to use iterators and more of the STL like this:

那么你可以重写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));
}

beyond that, you could use some macros to avoid retyping that a lot, but I'm not a huge fan of that. Personally, I like the multiple functions over the single one which takes a command string. As it gives you more versatility over how the decision is made.

除此之外,你可以使用一些宏来避免重新输入那么多,但我并不是那种*的粉丝。就个人而言,我喜欢使用命令字符串的单个函数。因为它为您提供了更多的决策权。

If you do go with a single function taking a param to decide which to do, I would use an enum and a switch like this, it would be more efficient than strings and a cascading if. Also, in your example you have the if to decide which to do inside the loop. It is more efficient to check outside the loop and have redundant copies of the loop since "which command" only needs to be decided once per call. (NOTE: you can make the command a template parameter if it is known at compile time, which it sounds like it is).

如果你选择使用一个函数来决定做什么,我会使用枚举和这样的开关,它会比字符串和级联更有效。此外,在您的示例中,您有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;
    }
};

Also, as you mentioned, it is fairly trivial to replace the &Small::whatever, what a member function pointer and just pass that as a parameter. You can even make it a template too.

另外,正如你所提到的,替换&Small :: whatever,成员函数指针并将其作为参数传递是相当简单的。你甚至可以把它变成一个模板。

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

Then you can do:

然后你可以这样做:

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

The nice thing about both this and the regular parameter methods is that Big doesn't need to be altered if you change small to have more routines! I think this is the preferred method.

关于这个和常规参数方法的好处是,如果你改变小以拥有更多例程,则不需要改变Big!我认为这是首选的方法。

If you want to be able to handle a single parameter, you'll need to add a bind2nd too, here's a complete example:

如果你想要能够处理单个参数,你还需要添加一个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);
}

#2


4  

If you're using the std library, you should take a look at for_each.

如果你正在使用std库,你应该看看for_each。

You mention that using function pointers in C++ might not be a good idea, but -- allowing your worry is speed -- you have to see if this is even a performance bottleneck area you're in, before worrying.

你提到在C ++中使用函数指针可能不是一个好主意,但是 - 让你担心的是速度 - 在担心之前你必须看看这是否是你所处的性能瓶颈区域。

#3


0  

Try boost::function and boost::bind:

尝试boost :: function和boost :: 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));
}