На этом шаге мы рассмотрим основные принципы использования функций в качестве объектов.
Функциональные аргументы, передаваемые алгоритмам, не обязаны быть функциями. Они могут быть объектами, которые ведут себя как функции. Такие объекты называются объектами функций, или функторами. Иногда объект функции справляется с ситуацией, в которой обычная функция не работает. Объекты функций часто используются в 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)
На первый взгляд непонятно, зачем это нужно. Возникает ощущение, что объекты функций - конструкция странная, непонятная, а то и вовсе бессмысленная. Действительно, объекты функций усложняют программу. Но они способны на большее, чем функции, обладая рядом несомненных достоинств.
На следующем шаге мы закончим изучение этого вопроса.