Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
84
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

257

(a) class cl1 { public:

c11( int=0 ); bool operator==(); bool operator!=(); bool operator<=(); bool operator<();

// ...

};

(b) class c12 { public:

c12( int=0 ); bool operator!=(); bool operator<=(); // ...

};

(с) class c13 { public:

int ival;

};

(d) class c14 { public:

c14( int, int=0 ); bool operator==(); bool operator!=(); // ...

}

6.5. Итераторы

Итератор предоставляет обобщенный способ перебора элементов любого контейнера как последовательного, так и ассоциативного. Пусть iter является итератором для какого-либо контейнера. Тогда

++iter;

перемещает итератор так, что он указывает на следующий элемент контейнера, а

*iter;

разыменовывает итератор, возвращая элемент, на который он указывает.

Все контейнеры имеют функции-члены begin() и end().

begin() возвращает итератор, указывающий на первый элемент контейнера.

end() возвращает итератор, указывающий на элемент, следующий за последним в контейнере.

for ( iter = container. begin();

iter != container.end(); ++iter )

Чтобы перебрать все элементы контейнера, нужно написать:

С++ для начинающих

258

do_something_with_element( *iter );

Объявление итератора выглядит слишком сложным. Вот определение пары итераторов

// vector<string> vec; vector<string>::iterator iter = vec.begin();

вектора типа string:

vector<string>::iterator iter_end = vec.end();

Вклассе vector для определения iterator используется typedef. Синтаксис vector<string>::iterator

ссылается на iterator, определенный с помощью typedef внутри класса vector, содержащего элементы типа string.

for( ; iter != iter_end; ++iter )

Для того чтобы напечатать все элементы вектора, нужно написать: cout << *iter << '\n';

Здесь значением *iter выражения является, конечно, элемент вектора.

В дополнение к типу iterator в каждом контейнере определен тип const_iterator, который необходим для навигации по контейнеру, объявленному как const.

#include <vector>

void even_odd( const vector<int> *pvec, vector<int> *pvec_even, vector<int> *pvec_odd )

{

// const_iterator необходим для навигации по pvec vector<int>::const_iterator c_iter = pvec->begin(); vector<int>::const_1terator c_iter_end = pvec->end();

for ( ; c_iter != c_iter_end; ++c_iter ) if ( *c_iter % 2 )

pvec_even->push_back( *c_iter ); else pvec_odd->push_back( *c_iter );

const_iterator позволяет только читать элементы контейнера:

}

Что делать, если мы хотим просмотреть некоторое подмножество элементов, например взять каждый второй или третий элемент, или хотим начать с середины? Итераторы поддерживают адресную арифметику, а значит, мы можем прибавить некоторое число к итератору:

vector<int>::iterator iter = vec->begin()+vec.size()/2;

iter получает значение адреса элемента из середины вектора, а выражение

С++ для начинающих

259

iter += 2;

сдвигает iter на два элемента.

Арифметические действия с итераторами возможны только для контейнеров vector и deque. list не поддерживает адресную арифметику, поскольку его элементы не располагаются в непрерывной области памяти. Следующее выражение к списку неприменимо:

ilist.begin() + 2;

так как для перемещения на два элемента необходимо два раза перейти по адресу, содержащемуся в закрытом члене next. У классов vector и deque перемещение на два элемента означает прибавление 2 к указателю на текущий элемент. (Адресная арифметика рассматривается в разделе 3.3.)

Объект контейнерного типа может быть инициализирован парой итераторов,

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

#include <vector> #include <string> #include <iostream>

int main()

{

vector<string> svec; string intext;

while ( cin >> intext ) svec.push_back( intext );

// обработать svec ...

копируемым.) Допустим, есть вектор:

}

Вот как можно определить новые векторы, инициализируя их элементами первого

int main() { vector<string> svec;

//...

//инициализация svec2 всеми элементами svec vector<string> svec2( svec.begin(), svec.end() );

//инициализация svec3 первой половиной svec vector<string>::iterator it =

svec.begin() + svec.size()/2; vector<string> svec3 ( svec.begin(), it );

//...

вектора:

}

С++ для начинающих

260

Использование специального типа istream_iterator (о нем рассказывается в разделе

#include <vector> #include <string> #include <iterator>

int mainQ

{

//привязка istream_iterator к стандартному вводу istream_iterator<string> infile( cin );

//istream_iterator, отмечающий конец потока istream_iterator<string> eos;

//инициализация svec элементами, считываемыми из cin; vector<string> svec( infile, eos );

//...

12.4.3) упрощает чтение элементов из входного потока в svec:

}

Кроме итераторов, для задания диапазона значений, инициализирующих контейнер, можно использовать два указателя на массив встроенного типа. Пусть есть следующий

#include <string> string words[4] = {

"stately", "plump", "buck", "mulligan"

массив строк:

};

Мы можем инициализировать вектор с помощью указателей на первый элемент массива и на элемент, следующий за последним:

vector< string > vwords( words, words+4 );

Второй указатель служит стражем”: элемент, на который он указывает, не копируется.

int ia[6] = { 0, 1, 2, 3, 4, 5 };

Аналогичным образом можно инициализировать список целых элементов: list< int > ilist( ia, ia+6 );

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

Упражнение 6.9

Какие ошибки допущены при использовании итераторов: