На этом шаге мы рассмотрим проблемы, возникающие при преобразовании итераторов.
Обычный итератор можно преобразовать в обратный. Естественно, такой итератор должен быть двунаправленным, но следует помнить, что в процессе преобразования происходит смещение логической позиции итератора. Рассмотрим следующую программу:
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include <iterator> #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; } int main(int argc, char* argv[]) { vector<int> coll; // Вставка элементов от 1 до 9 for (int i=1; i<=9; ++i) { coll.push_back(i); } // Поиск позиции элемента со значением 5 vector<int>::iterator pos; pos = find (coll.begin(),coll.end(),5); // Вывод значения, на которое ссылается итератор pos cout << ToRus("Значение, на которое ссылается pos: "); cout << *pos << endl; // Преобразование итератора в обратный итератор rpos vector<int>::reverse_iterator rpos(pos); cout << ToRus("Значение, на которое ссылается rpos: "); // Вывод значения, на которое ссылается обратный итератор rpos cout << *rpos << endl; getch(); return 0; } //---------------------------------------------------------------------------
Программа выводит следующий результат:
Рис.1. Результат работы приложения
Итак, если вывести значение, на которое ссылается итератор, а затем преобразовать итератор в обратный, значение изменится. И это не ошибка - так и должно быть! Такое поведение следует из того факта, что интервалы являются полуоткрытыми. Чтобы задать все элементы контейнера, необходимо установить второй итератор в позицию за последним элементом. Для обратного итератора эта позиция соответствует позиции перед первым элементом. Но, к сожалению, такая позиция может оказаться несуществующей. Контейнер не обязан гарантировать, что позиция перед его первым элементом является действительной. Кроме того, обычные строки и массивы тоже могут быть контейнерами, а язык не гарантирует, что адресация массива не начинается с нуля.
В итоге проектировщики обратных итераторов пошли на уловку: они "физически" обратили концепцию полуоткрытого интервала. Интервал, определяемый обратными итераторами, включает начало, но не включает конец. Тем не менее на логическом уровне обратные итераторы ведут себя, как обычные. Следовательно, существует различие между физической позицией, определяющей элемент, на который ссылается итератор, и логической позицией, определяющей значение, на которое ссылается итератор (рисунок 2).
Рис.2. Позиция и значение обратного итератора
Спрашивается, что должно происходить при преобразовании обычного итератора в обратный? Должен ли итератор сохранить свою логическую позицию (значение) или физическую позицию (элемент)? Как видно из предыдущего примера, выполняется второе условие, то есть логическая позиция смещается к предыдущему элементу (рисунок 3).
Рис.3. Преобразование обычного итератора pos в обратный итератор rpos
На следующем шаге мы закончим изучение этого вопроса.