【问题标题】:Iterating over vector and calling functions迭代向量和调用函数
【发布时间】:2010-10-02 07:01:25
【问题描述】:

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

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,但这会迫使我为每个函数创建一个类,这听起来有点矫枉过正。

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

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(旧编译器)。

【问题讨论】:

    标签: c++ stl foreach iteration


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      你可以重写 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>();
      

      this 和常规参数方法的好处在于,如果您将 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);
      }
      

      【讨论】:

      • 谢谢埃文。但是 std::mem_fn 是 boost 而不是 STL 的一部分。在我的情况下,也许还有另一种使用 for_each 的方法?那么不同签名的函数呢?
      • 哎呀,错字:mem_fn 是 boost 的一部分,但是 std::mem_fun 是 STL 的一部分。
      • 对于不同签名的函数,你可以使用几个模板函数来涵盖所有的可能性。
      • 在枚举示例中,使用 template void foo() { switch(cmd) { case A: /*bla*/ case B: /*bla */;}} 到避免任何运行时损失。
      【解决方案3】:

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

      【讨论】:

      • 一个很好的解决方案,(尽管你应该使用 std::for_each)。但不幸的是,他说 boost 是一个可行的选择。
      • 为什么投反对票?我在编辑之前回答说他不能使用 boost。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-02
      • 1970-01-01
      • 2020-01-03
      • 2011-08-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多