迭代向量并调用函数

发布于 2024-07-11 09:43:48 字数 1392 浏览 5 评论 0 原文

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

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++ 中使用函数指针是个好主意。

我也一直在考虑函子和 functionoids< /a>,但这会迫使我为每个函数创建一个类,这听起来有点矫枉过正。

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

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 而不能使用 boost(旧编译器)。

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.

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清晰传感 2024-07-18 09:43:49

如果您使用的是 std 库,则应该查看 for_each

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

If you're using the std library, you should take a look at 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.

盛装女皇 2024-07-18 09:43:49

尝试 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));
}

Try boost::function and 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));
}
寂寞美少年 2024-07-18 09:43:48

好吧,您可以重写 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>();

这个方法和常规参数方法的好处是,如果你改变较小的值以获得更多例程,则不需要改变 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);
}

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

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).

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.

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.

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

#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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文