Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 139
Текст из файла (страница 139)
Итераторное поле сиггеи< может указывать лишь на элементы своей последовательности плюс элемент «за последним элементом последовательности» (опе-раз(М))е-еп<( е1ещеп(). Но для класса гечегзе Йе< а<от этот элемент является элементом «перед первым элементом исходной последовательности», и к которому доступа нет. Чтобы избежать проблем с доступом, поле сиггеи( ссылается на элемент, расположенный за элементом, на который ссылается сам гечегзе Йега<ог. Для этого операция * возвращает значение * (си«тел<-1), а операция +«реализуется с помощью операции -- над полем си«геля Класс гете«ее Йети<от поддерживаетлишь те операции, которые поддерживаются возвращающим объект этого класса контейнером.
Например: чоЫ/'(тес<от<!п<>з ч, 1!11<сяаг>з Ь() ( ч.«Ье8!п () (31 = 7< По<с итератор произвольного доступа 1з(.«Ьее!п() 131 = '4'; Йегтот: двунаправленный итератор не поддерживает О * (««««««111.«Ьее!п () ) = '4' < тУо<(1 ) 19.2.6. Потоковые итераторы Обычно ввод/вывод выполняется с помощью библиотек потоков (глава 21), графического интерфейса пользователя (не стандартизованного в С++) или с помощью библиотек ввода/вывода языка С (921.8). Все эти средства ввода/вывода ориентированы в первую очередь на чтение/запись индивидуальных значений различных типов. Стандартная библиотека предоставляет четыре итераторных типа' для согласования потокового ввода/вывода с контейнерами и алгоритмами: < Также относятся к итераторным адаптерам.
— Прим. ред. Кроме того, для обратных итераторов стандартная библиотека предоставляет операции ==, ! =, «, =, », =, + и —. 19.2. Итерагоры и последовательности 665 ° оз(геат 1(ега(ог для записи в озпеат 53.4, 521.2.1). ° т(геа(п !(ега(ог для чтения из п(геат (53.6, 521.3.!). ° оз(геатЬи~' 1(ега(ог для записи в буфер потока (521.6.1). ° 1з(геатЬи(" 1(егагог для чтения из буфера потока (521.6.2). Центральная идея состоит в том, чтобы представить ввод и вывод коллекций значений в виде последовательностей: (етр1а(е<с1аьв Т, с1авз СЬ = сваг, с(ат Тг = слег (гайз<СЬ» с(азз озегеат пега(ог: риЫ!с дега(ог<ои(ри( Вега(ог (ад, воЫ, го!((, гоЫ, воЫ> ( риЫ1с: (уре((е) СЬ слег (уре( (уре((е1 тг (га!(з 1уре( (уре((е) Ьаз(с оз(геат<СЬ, Тг> оз(геат (уре( озегеат дега(ог (отгеат (уреь з); оз(геат !(ега(ог (оз(геат (уреь з, сопт СЬ" ((е1(т) оз(геат Ьега1ог(сопт аз(геат дега(огь) 1 -оз(геат !(ега(ог() 1 оз(геат пега(огь орега(ог= (сопл( Ть га!); 77 пишет ка! в поток вывода оз(геат Вега(огь орега(ог* () 1 оз(геат пега(огь орега(огь+ () 1 оз(геат !(ега(огь прего(ог++ (т() Этот итератор берет обычные операции записи и инкремента итератора вывода н преобразует их в операцию «над оз(геат.
Например: го!1(() ( отгеат (1ега(ог<т(> оз (сот) *Оз= 7; +ЕОЗ( *оз = 791 ) 77 пишет оп-ы в сои( через оз 77 выводим 7 (используя сош«7) 77 готовимся к следующему выводу 77 выводим 79 (етр1а(е<с1азв Т, с(ат СЬ = сЬаг, с1ат Тг = сваг (га((в<СЬ>, с1аьв О!з( = ри((ф' (> с(азз (з(геат пега(ог: риЫ!с Ьега1ог<(при( дега(ог (ае, Т, 17!в(, сопя( Т*, сопт Ть> Операция ьь или подготавливает реальную операцию вывода„или не делает ничего. Разные реализации могут осуществлять разные стратегии поведения, но в любом случае для переносимости кода операция н- должна присутствовать между любыми двумя присваиваниями в аякса(и 1(ега(ог.
Естественно, все стандартные алгоритмы написаны именно таким образом — иначе они не стали бы работать с контейнером типа вес(ог. Все зто объясняет причины, по которым аыгеат 1(егтог определен таким образом. Реализация озггеат 1(ега(ог тривиальна и оставляется в качестве упражнения (519.6[4]). Стандартный ввод/вывод поддерживает разные типы символов; сйаг йт(в (520.2) описывает аспекты символьных типов, важные для ввода(вывода и строк типа з(т1а».
Потоковый итератор ввода для !з(геат определяется аналогично: Глава 19. Итераторы и аллокаторы 666 ( риЫ<с: (ура<(ет" СЬ сйаг <уре! <уре((еу Тг <гааз (уре; (уре((еГЬая!с <з<геат<СЬ, Тг> Ьйеат <уре! << колеи ввода (я<сеет вега(ог (); Ь<геат Йега(ог((з1геат (урез з) 1 Ь(геат !1ега(ог(соля< (я<сеет ((его<ага) -Ь<геат Ьега<ог() 1 солт Ть орега<ог*() соля<( сои<и Т* орега(ог-> () сопя(1 Ьиеат !(ега(огь прего<ос++ (); Ьпеат ((ега<ог прего<ос++ ((п<) < )! Этот итератор определен таким образом, что обычные операции с контейнерами вызывают операцию» над Ьйеат. Например: гой(.Т() ( Ь(геат 11ега(ог<!и<> Ь (с<л) <п< <1 = *Ь( -~- я<я (л<!г = *Ь; ) о' читаем!п(-и из с!и через и << читаем (и( (используя с!и»!() ч' готовимся к следующему вводу (У читаем (и< Умолчательный 1огеат (<ега<ог обозначает конец ввода так, что мы можем явным образом задать входную последовательность: го!(< у (гес<ог<(л<> ь г) сору(Ь<геат вега(ог<!п<> (с(и), Ь(геат вега(ог<(п<> (), Ьасй 1изег(ег(г) ) ) Чтобы это работало, стандартная библиотека предоставляет для типа Ьй еат 1(Е- га(ог операции == и .=.
Реализация 1я<геат Ьега<ог уже не столь тривиальна, как реализация оя<геат Бега(ог, но все равно не слишком сложна, так что я и ее оставляю в качестве упражнения (919.6(5]). 19.2.6.1. Буфера потоков Как объясняется в 921.6, потоки ввода/вывода основаны на идее о типах оз<геат и Ьй еат, заполняюших и вычитываюших буфера, ввод/вывод в которые происходит на низком физическом уровне. Имеется возможность обойти форматирование стандартных потоков ввода/вывода и напрямую работать с их буферами (521.6.4). Такая возможность предоставляется и алгоритмам через понятие итераторов 1я(геатбау вега(ог и оя<геатЬиТ вега(ог: <етр!а(е<с(ат СЬ, с(ат Тг = сйаг й а!<я<СЬ» с!аз< <я<геатЬиу' вега(ог: рибдс вега(ог<(при( вега(ог <ае, СЬ, <урепате Тг::оД <уре, СЬ*, СЬь> 19.2, Итераторы и последовательности бб7 риЫ<с: <урейе~СЬ слог <уре< <урейе! Тг <га«и <уре< <урейеТ<урепате Тг<:1п« уре <и« уре; <урейе~Ьазк з<геатби7<СЬ, Тг> з<геатЬиу <уре; <урейеТЬаз<с <з<геат<СЬ, Тг> !з<геат <уре< У вспомогательный тип с1ат ргоху < !з<геатЬи/ Ьега<ог() <Ьго<г(); У конец буфера !з<геатЬи3' 1<в«агою (!гйгеат <урез 1з) <Ьго<г(] < У читаем из тгеатЬи1'объекта 1з (з<геатби~' дега<о«(з<геатби! <уре*) <Ьго<г (); <з<геатЬ|4' бега<о<(сопз< ргохуь р) <Ьго<г(); (<читаем из з<геатЬи!объекта р СЬ орега<ог* () сопз<< (з<геатЬи!' 1<его<ось орега<огп -.'- ( ); ргоху орега<ог++ (т<) < Ьоо! с<<ив! (!з<геатбву пега<ось ) )< Кроме того, предоставляются и операции == и .
'=. Чтение из зв еа<иЬи7'является более низкоуровневой операцией, чем из <з<геат. Как следствие, интерфейс 1з<«еатби/ !<е«аи<«более хаотичен, чем у и~еат 1<с«а<о«. Тем не менее, при должной инициализации объектов типа и«еатбау' йега<ог операции *,;ъ и = обладают при традиционном использовании традиционной семантикой. Тип р«оху является вспомогательным типом, допускающим постфиксную операцию ъъ без наложения ограничений на реализацию з<«еатЬи<.
При инкрементировании итератора ргоху хранит результирующее значение: <етр<а<е<с<азз СЬ, с1ат Тг = сбаг и а<и<СЬ» с<от 1з<геатЬиТ «ега<ог<СЬ, Тг>: <ргоху ( СЬ «а<< Ьамс з<геатЬиу<СЬ, Тг>* Ьиг< ргоху(СЬ г, Ьаяс з<геатбиу<СЬ, Тг>* Ь): га!(г), ЬиТ(Ь) () риЫ<с: СЬ орега<ог* () (ге<игл га1< ) Тип оз<«еатЬиг !<е«а<о«определяется аналогично: <етр<а<е <с1ат СЬ, с<от Тг = слог оа<уз<СЬ» с1ат оз<геатЬи!' Ьега<ог: риЫ<с Ье< а<о«ои<ри< пега<о« <ае, гоЫ, го<й, го<й, го!й> ( риЫ<с: <урейе!" СЬ сбаг <уре; <урейе3' Тг и ай< <уре< <урейеТЬамс з<геатби7<СЬ, Тг> з<геатби! <уре< <урейе!Ьаз(с оз<геат<СЬ, Тг> оз<геат <уре< оз<геатЬиГ пега<о«(оыгеат <урез оз) <Ьгош() < впишем в з<геатЬ<й'объекта оз оз<геатЬиу !<его<о« (з<геатЬи« уре* ) <Ьго<г ( ); ока еатЬи! пега<ага орега<ог= (СЬ); 668 Глава 19.
Итераторы и аллокаторы ов(геатйи< пега<ага орега<ог* () ( ов<геатЬи1 дега<ага прего<ос»» (); ов<геатЬи) вега<о<в орега(ог»в (<и<) Ьоо11а(<е<<() сопя <йгон (); )( ание если Тг:;во~9 виден 19.3. Итераторы с проверкой (етрйие<сй(я Соп(, с(а«(1<ее> Сйесйе«йег<Соп(,йег> <пайе сйесйе«(Соп(а с, 1(ег<) ( ге<игп Сйесйе(( лег<Сон<, 1(ег> (с, (') ( ) <етрЫе<сй((в Сон<> Сйесйе<< лег<Сон(, <урепате Соп(:: йегатг> тайе сйесйе(( (Сои<а с) ге(игп Сйесйе<< пег<Сои(,(урепате Соп«: дега(ог> (с,с.Ьерп () ) < Эти функции удобны с точки зрения выведения типов из их фактических аргументов (явного объявления типов при этом не требуется).
Например: гоЫ Г(гес(ог«п<>а г, сопя гес(ог<т<>а гс) ( <уре(<е1 Сйесйе(< лег< вес(ог<т(>, гес(ог<т(> <: дега(ог> С1; С1 р1 = тайе сйесйе(<(г, г. Ьед(п () «3) ( С1 р2 = <иайе сйесйе«(г) < (( по умолчанию: указывает на первый зл-т Кроме стандартных программист может определять и свои собственные итера- торы. Часто это требуется при создании новых контейнеров, но бывает необходимым и при новом способе использования уже существующего контейнера. В качестве примера я здесь рассматриваю итератор, выполняющий проверку диапазона при доступе к элементам своего контейнера.
Применение стандартных контейнеров уменьшает необходимость в прямых манипуляциях с памятью. Применение стандартных алгоритмов снижает количество прямых обращений к элементам контейнера. Применение стандартной библиотеки вкупе со средствами языка С++ по строгой проверке типов резко снижают количество ошибок во время выполнения программ по сравнению с результатами, типичными для программирования в стиле языка С. Однако стандартная библиотека по-прежнему полагается на программиста в ситуациях потенциального выхода за границы диапазона элементов контейнера.
Если по ошибке обратиться к элементу контейнера х как х [х.в(ге()» 7), то случится нечто непредвиденное и, обычно, малоприятное. Как мы знаем, здесь может в некоторых случаях помочь использование векторов с проверкой, таких как 1'ес (53.7.2). В более общем контексте требуется применение итераторов с проверкой. Чтобы на программиста не нала(алось при этом слишком больших дополнительных нагрузок, нам требуется итератор с проверкой и удобным способом его прикрепления к контейнеру. Чтобы построить О(есйе<1 йег, нам нужны контейнер и итератор к нему. Я также предоставляю функции для удобного создания Сйесйт1 йег (как в случае «связывающих» адаптеров в 518.4.4.1, итераторов для вставок из 519.2.4 и т.д.): ) 9.3.