Функциональные объекты против состояния

Я использовал функциональные объекты, чтобы узнать, что происходит с объектом, который передается, и как это влияет на состояние объекта. Вот фрагмент кода теста:

#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
 */

Когда я передаю объект с перегруженным оператором (), происходит несколько обращений к деструкторам; и никакие объекты не создаются! Так что я не могу полагаться, так сказать, на верность состояния объекта. Означает ли это, что когда я передаю объект функции (с перегруженным оператором ()) для обратного вызова, я должен предполагать, что состояние объекта не сохраняется? И это, предполагаемое поведение?

С другой стороны, обратный вызов связанной функции-члена изнутри объекта другого типа производит очень стабильное поведение (я не знаю, какой термин использовать); то есть я ожидал, что состояние объекта будет сохранено; и это действительно так! Это также предполагаемое поведение? IOW, это, как правило, понимают функторы?

PS:

Я также проверил это с помощью boost::function и boost::bind --- Результат очень похож. Вероятно, другая нить необходима для обсуждения нюансов.

2 ответа

Решение

О "нет конструкторов": есть вызовы конструктора копирования.

Попробуйте больше инструментов:

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. Это должно быть скопировано.
  • Bind, это предполагаемое поведение, если вы передаете значение. Призыв, который вы передаёте, может быть временным. Таким образом, стандартным поведением является копирование.

Вы можете избежать этого, используя справочную оболочку, если знаете, что ваш вызываемый объект будет существовать достаточно долго (как в вашем коде). Пытаться:

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

Когда я передаю объект с перегруженным оператором (), происходит несколько обращений к деструкторам; и никакие объекты не создаются!

Вы не учитываете объекты, созданные с помощью конструктора копирования, который создается компилятором, когда вы его не предоставляете.

Добавить конструктор копирования в 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"; }
};
Другие вопросы по тегам