【问题标题】:Function Objects vs State函数对象与状态
【发布时间】:2016-02-03 06:21:18
【问题描述】:

我已经使用函数对象来了解传递的对象会发生什么,以及它如何影响对象的状态。这是测试代码的sn-p:

#include <iostream>
//#include <boost/function.hpp>
//using boost::function;

#include <functional>
using std::function;
using std::bind;

struct A {
  A() { std::cout << "Creating..." << "\n"; }
  void operator()() { call(); }
  void call() { std::cout << "Executing call..." << "\n"; }
  virtual ~A() { std::cout << "Destroying" << "\n"; }
};

typedef function<void ()> Func;

struct B{
  Func f;
  B(Func ff) : f(ff) {}
  void call() {f();}
};

int main(int argc, char *argv[])
{
  {
    A a;
    B b(a);
    for (int i = 0; i < 5; ++i)
      b.call();
  }
  {
    A a2;
    B b2(bind(&A::call, &a2));
    for (int i = 0; i < 5; ++i)
      b2.call();
  }
  return 0;
}

/** Output **
  Creating...
  Destroying
  Destroying
  Executing call...
  Executing call...
  Executing call...
  Executing call...
  Executing call...
  Destroying
  Destroying
  Creating...
  Executing call...
  Executing call...
  Executing call...
  Executing call...
  Executing call...
  Destroying
 */

当我传递带有重载运算符()的对象时,会多次调用析构函数;并且没有对象被创建!所以,我不能依赖,也就是说,对象状态的保真度。这是否意味着当我传递一个函数对象(带有重载的operator())进行回调时,我应该假设对象的状态没有被保留? 这是预期的行为吗?

另一方面,从另一个类型的对象内部回调绑定的成员函数会产生一个非常稳定的行为(我不知道该用什么术语);也就是说,我希望保留对象状态;确实如此! 这也是预期的行为吗? IOW,这就是函子的普遍理解方式吗?

PS:

我还用 boost::function 和 boost::bind 检查了它——结果非常相似。可能需要另一个线程来讨论细微差别。

【问题讨论】:

    标签: c++ functor std-function stdbind


    【解决方案1】:

    关于“无构造函数”:有复制构造函数的调用。

    尝试更多检测:

    struct A {
      A() { std::cout << "Creating..." << "\n"; }
      void operator()() { call(); }
      A(const A&) { std::cout << "Copying" << "\n"; }
      A(A&&) { std::cout << "Moving" << "\n"; } // Assuming C++11
      void call() { std::cout << "Executing call..." << "\n"; }
      virtual ~A() { std::cout << "Destroying" << "\n"; }
    };  
    

    关于复制:

    • 您将可按值调用的对象交给 B 的构造函数。它必须被复制。
    • 绑定,如果你提交一个值,这是预期的行为。您要绑定的可调用对象可能是暂时的。因此,默认行为是复制。

    如果您知道您的可调用对象将存在足够长的时间(就像您的代码中的情况一样),您可以通过使用引用包装器来避免这种情况。试试:

    int main(int argc, char *argv[])
    {
      {
        A a;
        {
          B b(a);
        }
        std::cout << "-------------\n";
        B(std::ref(a));
        std::cout << "-------------\n";
        B(bind(&A::call, a));
        std::cout << "-------------\n";
        B(bind(&A::call, &a));
        std::cout << "-------------\n";
        B(bind(&A::call, std::ref(a)));
        std::cout << "-------------\n";
      }
      std::cout << "-------------\n";
      return 0;
    }
    

    【讨论】:

    • 很好的解释。对通过引用传递函子的理解,以及通过引用绑定函数的相似性是解释的典范!谢谢
    【解决方案2】:

    当我传递带有重载运算符()的对象时,会多次调用析构函数;并且没有对象被创建!

    您没有计算使用复制构造函数构造的对象,复制构造函数是在您不提供任何内容时由编译器创建的。

    将复制构造函数添加到A,您将看到对析构函数的调用次数与对构造函数的调用次数相同。

    struct A {
      A() { std::cout << "Creating..." << "\n"; }
    
      // Add this
      A(A const& copy) { std::cout << "Creating..." << "\n"; }
    
      void operator()() { call(); }
      void call() { std::cout << "Executing call..." << "\n"; }
      virtual ~A() { std::cout << "Destroying" << "\n"; }
    };
    

    【讨论】:

      猜你喜欢
      • 2011-11-16
      • 1970-01-01
      • 1970-01-01
      • 2021-04-10
      • 1970-01-01
      • 2020-07-01
      • 2010-09-23
      • 2021-12-02
      • 2019-03-22
      相关资源
      最近更新 更多