Программирование >>  Включение нужных заголовков 

1 ... 30 31 32 [ 33 ] 34 35 36 ... 71


Итераторы

\lt 1 .1 s -in

Ha первый взгляд итераторы представляются предметом весьма простым. Но стоит присмотреться повнимательнее, и вы заметите, что стандартные контейнеры STL поддерживают четыре разных типа итераторов: iterator, const iterator, reverse i terator и const reverse i terator. Проходит совсем немного времени, и выясняется, что в некоторых формах insert и erase только один из этих четырех типов принимается контейнером. И здесь начинаются вопросы. Зачем нужны четыре типа итераторов? Существует ли между ними какая-либо связь? Можно ли преобразовать итератор от одного типа к другому? Можно ли смешивать разные типы итераторов при вызове алгоритмов и вспомогательных функций STL? Как эти типы связаны с контейнерами и их функциями?

В настоящей главе вы найдете ответы на эти вопросы, а также поближе познакомитесь с разновидностью итераторов, которой обычно не уделяют должного внимания: istreambuf i terator. Если вам нравится STL, но не устраивает быстродействие istreain i terator при чтении символьных потоков, возможно, istreambuf iterator поможет справиться с затруднениями.

Совет 26. Старайтесь использовать iterator вместо const iterator, reverse iterator и const reverse iterator

Как известно, каждый стандартный контейнер поддерживает четыре типа итераторов. Для контейнера container<T> тип iterator работает как Т*, тогда как const i terator работает как const Т* (также встречается запись Т const*). При увеличении iterator или const iterator происходит переход к следующему элементу контейнера в прямом порядке перебора (от начала к концу контейнера). Итераторы reverse i terator и const reverse i terator также работают как Т* и const Т* соот-



ветственно, но при увеличении эти итераторы переходят к следующему элементу в обратном порядке перебора (от конца к началу).

Рассмотрим несколько сигнатур insert и erase в контейнере vector<T>:

iterator insert(iterator position, const T& x);

Iterator eraseCiterator position);

iterator eraseCiterator rangeBegin. iterator rangeEnd);

Аналогичные функции имеются у всех стандартных контейнеров, но тип возвращаемого значения определяется типом контейнера. Обратите внимание: перечисленные функции требуют передачу параметров типа iterator. Не consti terator, не reverse iterator и не const reverse iterator - только iterator. Хотя контейнеры поддерживают четыре типа итераторов, один из этих типов обладает привилегиями, отсутствующими у других типов. Тип iterator занимает особое место.

На следующей диаграмме показаны преобразования, возможные между итераторами разных типов.

constjterator,

iterator \base()

reverse Iterator-►const reverse iterator

base()

Из рисунка следует, что iterator преобразуется в const iterator и reverse iterator, a reversei terator - в const reverse i terator. Кроме того, reverse i terator преобразуется в iterator при помощи функции base типа reversei terator, a const reverse i terator аналогичным образом преобразуется в const i terator. Однако из рисунка не видно, что итераторы, полученные при вызове base, могут оказаться не теми, которые вам нужны. За подробностями обращайтесь к совету 28.

Обратите внимание: не существует пути от const iterator к iterator или от constreversei terator к reverse i terator. Из этого важного обстоятельства следует, что const i terator и const reverse i terator могут вызвать затруднения с некоторыми функциями контейнеров. Таким функциям необходим тип iterator, а из-за отсутствия обратного перехода от const-итераторов к iterator первые становятся в целом бесполезными, если вы хотите использовать их для определения позиции вставки или удаления элементов.

Однако не стоит поспешно заключать, что const-итераторы вообще бесполезны. Это не так. Они прекрасно работают с алгоритмами, поскольку для алгоритмов обычно подходят все типы итераторов, относящиеся к нужной категории. Кроме того, const-итераторы подходят для многих функций контейнеров. Проблемы возникают лишь с некоторыми формами i nsert и erase.

Обратите внимание на формулировку: const-итераторы становятся в целом бесполезными, если вы хотите использовать их для определения позиции вставки или удаления элементов. Называть их полностью бесполезными было бы неправильно. Const-итераторы могут принести пользу, если вы найдете способ получения iterator для consti terator или constreverseiterator. Такое возможно часто, но далеко не всегда, причем даже в благоприятном случае решение не очевидно, да и эффективным его не назовешь. В двух словах этот вопрос не изложить, если вас



iter i: Constlter ci:

и типами итераторов

i и ci указывают на элементы одного контейнера

if (i=ci)... Сравнить iterator

с constjterator

В данном примере происходит обычное сравнение двух итераторов контейнера, подобные сравнения совершаются в STL сплошь и рядом. Просто один объект относится к типу i terator, а другой - к типу const i terator. Проблем быть не должно - iterator автоматически преобразуется в const iterator, и в сравнении участвуют два const i terator.

Именно это и происходит в хорошо спроектированных реализациях STL, но в некоторых случаях приведенный фрагмент не компилируется. Причина заключается в том, что такие реализации объявляют operator= функцией класса const i terator вместо внешней функции. Впрочем, вас, вероятно, больше инте-

заинтересуют подробности - обращайтесь к совету 27. А пока имеющаяся информация позволяет понять, почему типу iterator отдается предпочтение перед его const- и reverse-аналогами.

Некоторым версиям insert и erase при вызове должен передаваться тип iterator. Const- и reverse-итераторы им не подходят.

Автоматическое преобразование const-итератора в iterator невозможно, а методика получения iterator на основании const i terator (совет 27) применима не всегда, да и эффективность ее не гарантируется.

Преобразование reversejterator в iterator может требовать дополнительной регулировки итератора. В совете 28 рассказано, когда и почему возникает такая необходимость.

Из сказанного следует однозначный вывод: если вы хотите работать с контейнерами просто и эффективно и по возможности застраховаться от нетривиальных ошибок, выбирайте iterator вместо его const- и reverse-аналогов.

На практике выбирать обычно приходится между iterator и const iterator. Выбор между iterator и reverse iterator часто происходит помимо вашей воли - все зависит от того, в каком порядке должны перебираться элементы контейнера (в прямом или в обратном). А если после выбора reverse i terator потребуется вызвать функцию контейнера, требующую i terator, вызовите функцию base (возможно, с предварительной регулировкой смещения - см. совет 28).

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

typedef deque<int> IntDeque; Определения типов

typedef IntDeque:iterator Iter: упрощают работу

typedef IntDeque::constjterator Constlter: с контейнерами STL



1 ... 30 31 32 [ 33 ] 34 35 36 ... 71

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика