Чтение онлайн

ЖАНРЫ

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

Для функций

min_element
и
max_element
необходим специальный функтор, принимающий два значения (они имеют тип объектов, на которые ссылается итератор) и возвращающий значение типа
Boolean
, показывающее, является ли первое значение меньше, чем второе. Функтор, который возвращает значение типа
Boolean
, называется предикатом. Рассмотрим, например, поиск самого большого элемента в наборе пользовательских типов (пример 11.4).

Пример 11.4. Поиск максимального элемента для пользовательских типов

#include <algorithm>

#include <vector>

#include <iostream>

using namespace std;

struct Chessplayer {

 ChessPlayer(const char* name, int rating)

: name_(name), rating_(rating) { }

 const char* name_;

 int rating_;

};

struct IsWeakerPlayer {

 bool operator(const ChessPlayer& x, const ChessPlayer& y) {

 return x.rating_ < y.rating_;

};

int main {

 ChessPlayer kasparov("Garry Kasparov", 2805);

 ChessPlayer anand("Viswanathan Anand", 2788);

 ChessPlayer topalov("Veselin Topalov", 2788);

 vector<ChessPlayer> v;

 v.push_back(kasparov);

 v.push_back(anand);

 v.push_hack(topalov);

 cout << "the best player is ";

 cout << max_element(v.begin, v.end, IsWeakerPlayer)->name_;

 cout << endl;

}

Программа

примера 11.4 выдает следующий результат.

the best player is Garry Kasparov (лучший игрок - Гарри Каспаров)

Функторы

Многие STL-алгоритмы в качестве параметров используют определенные пользователем объекты-функции и указатели на функции. И те и другие называются функторами (functors). Иногда в литературе термин «объект-функция» используется как синоним термина «функтор», однако я использую термин «объект-функция» для обозначения только экземпляров класса или структур, которые перегружают

operator
. Какой из двух типов функторов лучше использовать? В большинстве случаев объект-функция более эффективен, потому что большинство компиляторов могут легко его реализовать в виде встроенной функции.

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

Наконец, объекты-функции могут определяться внутри другой функции или класса. Указатели на функции приходится объявлять в области видимости пространства имен.

В примере 11.4 я показал, как в функции

max_element
можно использовать пользовательский предикат. Этот предикат является объектом-функцией
IsWeakerPlayer
.

Альтернативой пользовательскому предикату, показанному в примере 11.4, является перегрузка оператора

operator<
для структуры
ChessPlayer
. Это хорошо работает в определенных случаях, но предполагает, что самой важной является сортировка игроков по рейтингу. Может оказаться, что более распространенной является сортировка по именам. Поскольку в данном случае выбор метода сортировки может быть произвольным, я предпочитаю не определять оператор
operator<
.

11.3. Вычисление суммы и среднего значения элементов контейнера

Проблема

Требуется вычислить сумму и среднее значение чисел, содержащихся в контейнере.

Решение

Для расчета суммы можно

использовать функцию
accumulate
из заголовочного файла
<numeric>
и затем разделить ее на количество элементов, получая среднее значение. Пример 11.5 демонстрирует, как это можно сделать, используя вектор.

Пример 11.5. Вычисление суммы и среднего значения элементов контейнера

#include <numeric>

#include <iostream>

#include <vector>

using namespace std;

int main {

 vector<int> v;

 v.push_back(1);

 v.push_back(2);

 v.push_back(3);

 v.push_back(4);

 int sum = accumulate(v.begin, v.end, 0);

 double mean = double(sum) / v.size;

 cout << "sum = " << sum << endl;

 cout << "count = " << v.size << endl;

 cout << "mean = " << mean << endl;

}

Программа примера 11.5 выдает следующий результат.

sum = 10

count = 4

mean = 2.5

Обсуждение

Как правило, функция

accumulate
обеспечивает самый эффективный и самый простой способ вычисления суммы всех элементов, содержащихся в контейнере.

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

Пример 11.6. Обобщенная функция по расчету среднего значения

template<class Iter_T>

double computeMean(Iter_T first, Iter_T last) {

 return static_cast<double>(accumulate(first, last, 0.0))

/ distance(first, last);

}

Функция

computeMean
из примера 11.6 подойдет в большинстве случаев, но она имеет одно ограничение: не работает она с такими итераторами ввода, как
istream_iterator
.

Итераторы istream_iterator и ostream_iterator

Шаблоны классов

istream_iterator
и
ostream_iterator
представляют собой специализированные итераторы, определенные в заголовочном файле
<iterator>
которые позволяют рассматривать потоки как однопроходные контейнеры.

istream_iterator
является итератором ввода, который выступает в роли оболочки такого потока ввода, как
cin
или
ifstream
, позволяя использовать его в качестве параметра во многих обобщенных функциях.
ostream_iterator
является итератором вывода, который позволяет использовать потоки вывода, как будто они являются контейнерами. Использование итераторов
istream_iterator
и
ostream_iterator
является хорошей привычкой, так как с их помощью легче создавать повторно используемый программный код

Итератор

istream_iterator
позволяет выполнить только один проход по данным, поэтому вы можете вызвать либо
accumulate
, либо
distance
, но если вы вызываете обе функции, данные становятся недействительными, и всякая последующая попытка их просмотра, вероятно, приведет к неудаче. Пример 11.7 показывает, как можно написать более обобщенную функцию по расчету среднего значения за один проход последовательности чисел.

Пример 11.7. Более обобщенная функция по расчету среднего значения

Поделиться с друзьями: