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

2.3. Итераторы

Понимание итераторов является ключом к пониманию STL и тому, как наилучшим

образом работать с этой библиотекой. Обобщенные алгоритмы STL написаны с применением в качестве параметров итераторов, а контейнеры STL редоставляют итераторы, которые затем могут "включаться" в алгоритмы, как это было показано на рис. 1.1. На рис. 2.1 вновь показано это взаимоотношение, а также отношения между другими важными категориями компонентов STL. Эти очень обобщенные компоненты спроектированы для "подключения" друг к другу несметным количеством разных способов для получения больших и более

специализированных компонентов, которые можно найти в других библиотеках. Основным типом "проводов" для соединения компонентов является категория, именуемая итераторами (на рис. 1.1 и 2.1 эта категория схематически показана в виде соединительных шлейфов, а иерархия самих итераторов показана на рис. 2.2). Одним из видов итераторов является обычный указатель C++, но могут быть и иные итераторы, отличные от указателей. Такие другие типы итераторов, однако, должны вести себя как указатели в том смысле, что должны поддерживать операции наподобие ++ и *, причем ожидается, что эти операции ведут себя так же, как и с указателями: например, ++i перемещает итератор i к следующему положению, a *i возвращает местоположение, где может быть сохранено значение (т.е. можно записать * i = x), или значение из которого может быть использовано в выражении (как в х = * i).

Рис. 2.1. Пять из шести основных категорий компонентов STL (не показаны

аллокаторы)

Рассмотрим обобщенный алгоритм STL accumulate. При его вызове с итераторами

first и last и значением init, accumulate(first, last, init); добавляет к init значения в позициях от first до last (не включая значение в последней позиции) и возвращает получившуюся сумму. Например, мы можем написать следующую программу для вычисления и вывода суммы значений вектора.

Пример 2.11. Демонстрация обобщенной функции accumulate

#include <iostream>

#include <vector>

#include <cassert>

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

using namespace std;

int main()

{

cout << "Демонстрация функции accumulate." << endl;

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

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

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

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

assert (sum == 28);

cout << " Ok." << endl;

return 0;

}

Эта программа использует функцию accumulate для суммирования целых чисел из

контейнера vector1, который указывается с применением итераторов

vector1.begin () и vector1.end (). Можно воспользоваться функцией accumulate для работы непосредственно с массивом х, записав

sum = accumulate(&x[0], &х[5], 0);

или, например, со списком чисел с плавающей точкой double, как в следующем фрагменте:

double у[5] = {2.0, 3.0, 5.0, 7.0, 11.0};

list<double> list1(&у[0], &у[5]);

double sum = accumulate (list1.begin () , list1.end (), 0.0);

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

Давайте рассмотрим, как функция accumulate использует итераторы. Она может быть определена следующим образом:

template <typename InputIterator, typename T>

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

{

while (first != last)

{

init = init + *first;

++first;

}

return init;

}

Операции, выполняемые над итераторами, включают проверку неравенства итераторов с помощью оператора ! =, разыменование с применением оператора *, и инкремент с применением префиксного оператора ++. Эти операции, вместе с постфиксным оператором ++ и проверкой равенства ==, составляют все множество операций, поддержка которых требуется категорией входных итераторов. Другая характеристика входных итераторов (из-за которой они, собственно, и получили свое имя) — это то, что от операции разыменования *

требуется только возможность чтения из указанной позиции контейнера, но не записи в нее. В случае выходных операторов, наоборот, от операции разыменования * требуется только возможность записи в указанную позицию контейнера, но не чтения из нее; кроме того, поддержка проверок равенства и неравенства в этом случае не требуются.

STL определяет три другие категории итераторов — однонаправленные, двунаправленные итераторы и итераторы с произвольным доступом. За исключением входных и выходных итераторов, соотношения между итераторами образуют иерархию, показанную на рис. 2.2; т.е. каждая категория добавляет новые требования к требованиям, предъявляемым предыдущей категорией, что означает, что итераторы более поздней категории в иерархии одновременно являются членами более ранней. Например, двунаправленные итераторы

одновременно являются однонаправленными, а итераторы с произвольным доступом одновременно являются двунаправленными и однонаправленными.

Рис. 2.2. Иерархия категорий итераторов STL

Алгоритмы, такие как accumulate, find или merge, которые спроектированы для

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

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