Шаг 115.
Библиотека STL.
Объекты функций. Понятие объекта функции

    На этом шаге мы рассмотрим основные принципы использования функций в качестве объектов.

    Функциональные аргументы, передаваемые алгоритмам, не обязаны быть функциями. Они могут быть объектами, которые ведут себя как функции. Такие объекты называются объектами функций, или функторами. Иногда объект функции справляется с ситуацией, в которой обычная функция не работает. Объекты функций часто используются в STL, причем некоторые являются очень полезными.

Понятие объекта функции

    Объекты функций - еще один пример унифицированного программирования и концепции чистой абстракции. В соответствии с этой концепцией все, что ведет себя как функция, является функцией. Следовательно, если определить объект, который ведет себя как функция, он может использоваться в качестве функции. Но как понимать фразу "ведет себя как функция"? Ответ: это означает возможность вызова с круглыми скобками и передачей аргументов. Пример:

  function(arg1,arg2);  // Вызов функции

    Следовательно, если мы хотим, чтобы объект вел себя подобным образом, необходимо обеспечить возможность его "вызова" в программе с круглыми скобками и передачей аргументов. Да, такое вполне возможно (в C++ вообще редко встречается что-нибудь невозможное). От вас лишь потребуется определить оператор () с соответствующими типами параметров:

class X {
  public:
  // Определение оператора "вызов функции" 
  возвращаемое_значение operator() (аргументы) const;
  .   .   .   .
};

    Теперь объекты этого класса ведут себя как функции и могут вызываться в программе:

  X fo;
  .   .   .   .
  fo(arg1,arg2);       // Вызов оператора () для объекта функции fo 

    Такой вызов эквивалентен следующему:

  fo.operator()(arg1,arg2);  // Вызов оператора () для объекта функции fo

    Ниже приведен более полный пример - версия рассмотренной ранее программы с обычной функцией (шаг 111), переработанная для использования объекта функции:

//---------------------------------------------------------------------------

#include <vcl.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <conio.h> //необходимо для getch()

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;

std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

// Простой объект функции для вывода передаваемого аргумента
class PrintInt {
  public:
    void operator() (int elem) const {
      cout << elem << ' ';
    }
};

int main(int argc, char* argv[])
{
  vector<int> coll;
  // Вставка элементов со значениями от 1 до 9
  for (int i=1; i<=9; ++i) {
    coll.push_back(i);
  }

  // Вывод всех элементов
  cout << ToRus("Элементы вектора:\n");
  for_each (coll.begin(),coll.end(), // Интервал
    PrintInt());                     // Операция
  cout << endl;

  getch();
  return 0;
}
//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Результат выполнения программы выглядит так:


Рис.1. Результат работы приложения

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

  for_each (coll .begin(), coll.end(), 
    PrintInt());

    Алгоритм for_each() определяется примерно так:

namespace std {
  template <class Iterator, class Operation>
  Operation for_each (Iterator act, Iterator end, Operation op)
  {
    while (act != end) {  // Пока не достигнут конец интервала
      op(*act);           // - вызвать ор() для текущего элемента
      ++act;              // - переместить итератор к следующему
    }                     // элементу.
    return op;
  }
}

    Алгоритм for_each() использует временный объект функции ор, чтобы для каждого элемента делать вызов op(*act). Если третий параметр является обычной функцией, она просто вызывается с аргументом *act. Если третий параметр является объектом функции, вызывается оператор () объекта функции ор с аргументом *act. Таким образом, в приведенной программе алгоритм for_each() вызывает операторную функцию

  PrintInt::operator()(*act)

    На первый взгляд непонятно, зачем это нужно. Возникает ощущение, что объекты функций - конструкция странная, непонятная, а то и вовсе бессмысленная. Действительно, объекты функций усложняют программу. Но они способны на большее, чем функции, обладая рядом несомненных достоинств.

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг