Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STL_2.doc
Скачиваний:
5
Добавлен:
01.05.2025
Размер:
512.51 Кб
Скачать

2.4. Функциональные объекты

Рассматривавшаяся в предыдущем разделе функция accumulate является весьма

обобщенной в смысле использования ею итераторов, но не настолько обобщена, как могла бы быть, в смысле действий с типами значений, на которые указывают итераторы (эти типы называются просто типами значений итераторов). Определение accumulate предполагает, что имеется определенный для типа значения оператор +, используя его в выражении

init = init + *first;

Таким образом, функция может работать с любыми встроенными числовыми типами C++ или с любыми пользовательскими типами Т, в которых определен такой оператор. Однако абстрактное понятие накопления (accumulation) применимо не только к суммированию; можно точно так же накапливать, например, произведение значений последовательности. Потому STL предоставляет еще одну, более обобщенную версию accumulate:

template <typename Inputlterator, typename T, typename BinaryOperation>

T accumulate(InputIterator first, Inputlterator last, T init, BinaryOperation binary_op)

{

while (first != last)

{

init = binary_op(init, *first);

++first;

}

return init;

}

Вместо записи с оператором + в этом определении вводится еще один параметр — binary_op, представляющий собой бинарную операцию, используемую для объединения значений.

Как воспользоваться этой более обобщенной версией accumulate для вычисления произведения? Если мы определим функцию mult так, как это сделано в следующей программе, то сможем использовать ее в качестве параметра binary_op функции accumulate.

Пример 2.12. Использование обобщенного алгоритма accumulate для

вычисления произведения

#include <iostream>

#include <vector>

#include <cassert>

#include <numeric> // Алгоритм accumulate

using namespace std;

int mult(int x, int y) { return x * y; }

int main()

{

cout << "Использование обобщенного алгоритма "

<< "accumulate для вычисления произведения."

<< endl;

int x[5] = {2, 3, 5, 7, 11};

// Инициализация вектора элементами от х[0] до х[4]:

vector<int> vector1(&x[0], &х[5]);

int product = accumulate(vector1.begin(), vector1.end(), 1, mult);

assert (product == 2310);

cout << " Ok." << endl;

return 0;

}

(Обратите внимание, что мы заменили начальное значение с 0 на 1, которое является правильным "единичным элементом" для умножения.) В приведенном примере мы передаем аргументу accumulate обычную функцию mult. В действительности передается адрес функции, так что мы можем записать аргумент явно, как &mult. Но это только один из способов поддержки в C++ передачи функций другим функциям. Более общей является передача

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

Пример 2.13. Использование обобщенного алгоритма accumulate для

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

#include <iostream>

#include <vector>

#include <cassert>

#include <numeric> // Алгоритм accumulate

using namespace std;

class multiply {

public:

int operator()(int x, int y) const { return x * y; }

};

int main()

{

cout << "Использование обобщенного алгоритма "

<< "accumulate для вычисления произведения."

<< endl;

int x[5] = {2, 3, 5, 7, 11};

// Инициализация вектора элементами от х[0] до х[4]:

vector<int> vector1(&x[0], &х[5]);

int product = accumulate(vectorl.begin(),

vectorl.end(), 1,

multiply());

assert (product == 2310);

cout << " Ok." << endl;

return 0;

}

Путем определения оператора вызова функции operator () в классе multiply мы

определяем тип объекта, который может быть применен к списку аргументов, так же, как и функция. Обратите внимание, что объект, переданный accumulate, получается путем вызова конструктора класса по умолчанию multipl(), который автоматически создается компилятором, так как явно определенного конструктора в определении класса нет. Заметим также, что этот объект не требует для хранения памяти, так как представляет собой только определение функции (хотя в некоторых случаях хранение данных в функциональных объектах оказывается удобным).

В чем же преимущество, если таковое имеется, функциональных объектов перед

обычными функциями? Детально этот вопрос будет рассмотрен в главе 8, "Функциональные объекты", но одно из главных преимуществ заключается в том, что объекты, в отличие от обычных функций, могут хранить дополнительную информацию, которая затем может использоваться обобщенными алгоритмами или контейнерами, которым требуется более сложные знания о функции, чем алгоритму accumulate. Имеются также преимущества, связанные с эффективностью и обобщенностью.

Перед тем как завершить рассмотрение этой темы, мы должны упомянуть, что в

примере 2.13 в действительности нет необходимости определять класс multiply, поскольку STL уже содержит такое определение, хотя и в более обобщенной форме:

template <typename T>

class multiplies : public binary_function<T, T, T> {

public:

T operator()(const T& x, const T& y) const

{

return x * y;

}

};

Этот класс наследует другой компонент STL, бинарную функцию, назначение которой — хранить дополнительную информацию о функции (этот вопрос будет рассматриваться в главе 8, "Функциональные объекты"). Используя это определение, программу можно переписать следующим образом.

Пример 2.14. Использование обобщенного алгоритма accumulate для

вычисления произведения с применением multiplies

#include <iostream>

#include <vector>

#include <cassert>

#include <numeric> // Алгоритм accumulate

#include <functional> // Класс multiplies

using namespace std;

int main()

{

cout << "Использование обобщенного алгоритма "

<< "accumulate для вычисления произведения."

<< endl;

int x[5] = {2, 3, 5, 7, 11};

// Инициализация вектора элементами от х[0] до х[4]:

vector<int> vector1(&х[0], &х[5]);

int product = accumulate(vector1.begin(),

vector1.end(),

1, multiplies<int>() ) ;

assert (product == 2310);

cout << " Ok." << endl;

return 0;

}

Выражение multiplies<int> () представляет собой вызов конструктора по

умолчанию класса multiplies, инстанцированного с типом int. Несколько других

функциональных объектов показано в четвертом столбце на рис. 2.1.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]