Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 160
Текст из файла (страница 160)
Сброс потока Ы!геат (синхронизация ввода с буфером) выполняется функцией зупс() . Это не всегда можно выполнить корректно. Для некоторых видов потоков нам пришлось бы при этом повторно считать символы с реального источника — это и не всегда возможно, и не всегда желательно. Функция зупс () при успешном выполнении возвращает д, а в противном случае возвращает -1 и устанавливает !оз Ьазе:: Ьаз1Ы! Я21.3.3).
Снова напомним, что установка Ьаг(Ь!! может вызвать исключение (521.3.6). Функция зупс() для буфера, связанного с потоком вывода, сбрасывает содержимое буфера на устройство вывода. Операции» и ие!(), нацеленные на чтение из з!геатЬп(; интересны в первую очередь разработчикам средств ввода/вывода. Только разработчикам следует напрямую манипулировать буферами зв еатЬи~ Функция геев!зоте() реализует низкоуровневую операцию, позволяющую пользователю «заглянуть в поток» и посмотреть, есть ли в нем символы, доступные для чтения.
Это может быть полезно тогда, когда нежелательно дожидаться ввода, например с клавиатуры. См. также функцию гп агаИ() 521.6.4). 21.6.3. Потоки и буферы Связь между потоком и его буфером поддерживается классом Ьаяс юк !етр!а!е<с1азз СЬ, с(азз Тг = сйаг !га!ь<СЬ» сйзя Ьазк !оз: риЫи юз базе риЫ!с: д ...
Ьазк зиеатЬи(<СЬ, Тг> * и!Ьи1() сопя; д получить буфер дустановить буфер, очистить, и вернуть указатель на старый буфер: Ьагк зггеатЬи(<СЬ, Тг>" и!Ьи1(Ьаяс з(геатЬи1<СЬ, Тг>* Ь) ! дустановить локализацию (и вернуть старую) !оса!е ипЬие (сопя !оса)еь !ос) ! сваг паггот (сваг гуре с, сваг й) сопзгз д получить сваг из сйаг гуре сйаг !уре нЫеп(сваг с) сопл!! д получить сйаг (уре из сйаг д ... рго!вогез!: Ьаяс !оз(); соЫ тй(Ьаяс зггеатвир<СЬ, Тг>* Ь) ! д задать начальный буфер Кроме чтения и установки ззуеатЬи7'для потока (521.6.4), класс Ьаяс юз предоставляет функцию ЬпЬие(), чтобы читать или фиксировать национальные черты (локализация, интернационализация) потока 521.7) посредством вызова гтЬие() для его юз Ьазе (521.7.1) и вызова риЬЬпЬие () для буфера потока 521.6.4).
760 Глава 21. Потоки Функции лагком() и мЫеп() используются для преобразования символов типа сЬаг к символьному типу буфера сйаг гуре и обратно. Вторым аргументом вызова паггою(с, Ж служит символ типа сЬаг, возвращаемый функцией в том случае, когда невозможно преобразовать символ с типа сЬаг (уре к типу сЬаг, 21.б.4.
Буфера потоков гетр1агесс1аяз СЬ, с1ат 7г = сваг аа!ь<СЬ> сйьзв Ьаяс зггеатЬву' рпиесикг: СЬ* еЬасй() сопле; СЬ* аргг() сопле; СЬ* щи() сопев Р начало ае(-буфера У следующий заносимый символ д оперев 1-епй для аег-буф ера ыоЫ яЬитр ((пг и ); У добавить п к ерггО гоЫ жги(СЬ* Ьед(п, СЬ* пехг, СЬ* еай); дзадать едасй(2 яр(гО и еяр(гО СЬ* рЬаве () сопле; СЬ* рргг() сопл(; СЬ* ерра () сопзг; гоЫ рбитр (тг п ); гоЫзегр(СЬ* Ьерп, СЬ* спи) У начало ри(-буфера й' следующий свободный сйаг П опе-раз(-ет( для риибуфера й добавить и к рр(гО д установить рбсаеб и рр!гО на начало, а деррида - на конец Операции ввода/вывода специфицируются безо всякой связи с типами файлов, однако не со всеми устройствами можно обращаться одинаково в смысле буферизации.
Например, поток овГгеат, связанный со строкой типа згг(п» ($21.5.3), нуждается в буфере иного типа, чем овггеат, связанный с файлом (з21.5.1). Эта проблема решается предоставлением буферов разных типов во время инициализации потоков разных типов. Для работы со всеми типами буферов предоставляется единственный набор функций, так что озггеат не содержит кода, позволяющего различить эти типы. Различные типы буферов являются производными от класса зггеатЬиу", который предоставляет виртуальные функции для выполнения операций с разными стратегиями, например, функции для обработки переполнения и исчерпания. Класс Ьав1с вггеатЬиУ задает два интерфейса.
Открытый интерфейс предназначен в первую очередь для программистов, реализующих потоки вроде (я(гааза, ов» еат, увггеат, вгг(паз*еат и т.д. Кроме того, предоставляется защищенный интерфейс для тех, кто реализует новые стратегии буферизации и классы типа згеатЬи7 для новых типов источников ввода и устройств вывода. Для понимания зггеатЬи7'будет полезно сначала рассмотреть лежащую в его основе концепцию буферной области, доступ к которой осуществляется через защищенный интерфейс. Представим, что зггеатЬау' располагает областью записи (риг агеа) и областью чтения (лег агеа), с которыми работают операции «и», соответственно.
Каждая их этих областей характеризуется указателем на начало, текущим указателем и указателем на элемент, расположенный за концевым элементом (опе-раз(-())е-епб ро)п(ег). Эти указатели доступны через следующие функции: Тб1 2).б. Буферирование Располагая массивом символов, функции зе!р () и хе!я() могут установить значения указателей. Реализация может обратиться к области чтения (яе( агеа) следующим образом: (етр(азе<с(азз СЬ, с!азз Тг = сйаг !га(уз<СЬ» Ьаз!с з!геатЬиЯСЬ, Тг>::т! (уре Ьагйс зггеатЬиг<СЬ, Тг>::зпехгс () //пропустить текущий символ и прочитать последующий (((! < едргг О -ер! ( ) ) // если в буфере не меньше двух символов еЬитр (1) ! /У пропустить текущий символ ге!игл Тг::го 1лг (уре(*ер!г() ); У вернуть новый текущий символ ) // если в буфере ровно один символ // пропустить текущий символ (г(! == еер!г() -ер!г() ) еЬитр (1); ге!игл ипйефош (); ) // буфер пуст (или отсутствует), пробуем заполнит|к (Т(Тг::ец 1и! (уре(и((ош(), Тг::ео(() ) ) ге!игл Тг::ео(() (Т(0< еергг() -др!г() ) ге!игл Тг::го тг (уре(*бр! () ) ! ге!игл ипйе>у)ош (); ) г(ггиа! -Ьа|1| ззгеатби( ( ); 1оса1е риЬ!тЬие (сопл! !оса!еа 1ос) ! //установить локализацию (вернуть старую) 1оса!е ее!!ос () сола!! // получить локализацию Ьаз!с з!геатЬи~* рибзе!ЬиТ(СЬ* р, з!геатз(зе и) ! У задать разл~ер буфера роз гуре риЬ|еейоЯ (оЯ' !уре о!)г, !оз базе:: зеейй(г шау, // но|ицил (З2!.б.!) (оз Ьазе::орептойе т = (оз Ьазе::ш((оз Ьазе::ои!); роз гуре риЬ|еейроз (роз гуре р, !оз Ьазе::орелтойе т = (оз Ьазе::(п(1оз Ьазе::оий; 1пг риЬ|упс (); // ввод зупсО (~21.б.2) У пропустить текущ.
символ, в||ть следующий /У продвинуть ер!г() на 1 !и! гуре злех1с(); !п! (уре |Ьитрс () Доступ к буферу осуществляется через лргг(); функция еяргг() маркирует границу области чтения. Символы читаются из реального источника функциями иЯолО и лайем/3олО. Вызовы зга!м !уре::го 1лг !уре() гарантируют, что этот код не зависит от истинного типа символов. Представленный код допускает множество типов потоковых буферов и учитывает, что виртуальные функции и22о!в О и лпйег(2ол О могут перейти к новой области чтения (с помощью хе!я() ).
Открытый интерфейс зггеатЬлу выглядит следующим образом: гетр(иге< с)азз СЬ, |!аз| Тг = сйаг гга(о<СЬ» с(ат Ьаз(с з!геатЬи!' ( риЬ1(с: /У обычные (урейе(Я2!.2.!) Глава 2(. Потоки 762 1п( (уре лаегс(); УУ получить текущий символ яееатясе зие(п (СА* р, вгееатяхе п); УУ прочитать в р(О)..р[п-() УУ поместить с обратно в буфер 62!.6.2) УУ отменить чтение последнего символа (п( (уре ври(Ьасйс (СЬ с) 1пг (уре липее!с(); У[ри( с )[записать в р[О)..р(п-(] Р ввод готовр 1пг (уре ври1с(СЬ с); зггеатз(ге лри(п (сопле СЬ* р, в(геатв(се и ); згееатясе т акай(); УУ ...
)' УУ ее([) не заблокирует (Т (с(п . п1Ьи(( ) ->(и аиий ( ) ) с(п.аег(с) г У что-нибудь сделать ) е1зе УУ сделать что-нибудь еще ) УУ ее(() может заблокировать Отметим, что на некоторых системах бывает трудно определить, доступно ли что-нибудь для ввода. Таким образом, реализация (неудачная) функции т аиа11() могла бы возвращать нуль в случаях, когда предполагается успешность операции ввода. В дополнение к открытому интерфейсу, который используется классами Ьав(с 1вгтеат и Ьаяс ов(геат, класс Ьал[с лгееатЬи( предлагает защищенный интерфейс для разработчиков потоковых буферов.
Именно тут и объявляются виртуальные функции, определяющие политику буферизации: Открытый интерфейс содержит функции для вставки символов в буфер и извлечения их из буфера. Код этих функций прост и легко допускает встраивание, а это важно для эффективности. Функции, которые реализуют часть специфической стратегии буферизации, вызывают соответствующие функции защищенного интерфейса.
Например, риЬвегЬи[() вызывает функцию ветЬи[(), которая замещается в производных классах с целью реализации специфической стратегии этого класса в получении памяти для буферизации символов. Применение двух функций дпя реализации операции вроде вегЬи('() позволяет разработчику ов«еат выполнить некоторые вспомогательные действия до и после пользовательского кода. Например, разработчик может «обернуть» вызов виртуальной функции «у-блоком и перехватить исключения, возникающие в пользовательском коде. По умолчанию, ле(Ь4(0, О) означает отсутствие буферизации, а вегЬи$(р, и) означает применение массива р(0) ..р(п-1) для хранения буферизуемых символов.
Вызов т аиа11() используется для того, чтобы узнать, сколько символов находится в буфере. Это может потребоваться для того, чтобы не ждать ввода. При считывании из потока, связанного с клавиатурой, с(и.иег(с) может ожидать пользователя, пока тот не вернется с обеда. На некоторых системах и в некоторых приложениях разумно учесть это обстоятельство при чтении. Например: 763 2 ) .б. Буферирование гетр!а!е<с1ат СЬ, с!ат Тг = сйаг !га!зз<СЬ» с!азз Ьаяс з!геатЬи!' ( рго!ес!еа: // ... Ьаяс з!геатЬиТ() ! «рвиа! но(а' йиЬие (сопл! !оса!еь 1ос); //задать локализацию н(ггиа! Ьаяс з!геатЬи1* зегЬи/(СЬ* р, з!геатз!зе и); и!ггиа! роз гуре зеейо1)г(о)Т !уре о11, !оз Ьазе::зееИи иау, юз Базе::ореитоь!е т = юз Ьазе::!п ) 1оз Ьазе::оин ! тзеиа1 роз гуре зеейроз (роз ьуре р, !оз Ьазе::орептогге т = !оз базе::!п) юз Ьазе::ои!) / //ввод зупс() Д2!.б 2) Ызвиа! Ьи! луис ( ) н)гзиа! йи зйоивиапус ( ); и!гзиа! ззгеатз!ге кзде!п (СЬ*р, з!геатз(зе и); // получить и символов нйзиа1 1я !уре ипйег1!огн () и!г!иа! 1п! гуре и!!оп ( ) ! ягеиа1 ии гуре рЬасЬ1а!1 (!п! !уре с = Тг:: еоу() ); и!г!иа1 з!геатзяе кери(п (сопл! СЬ* р, з!геатз!ее п); //заиисать и символов нгеиа1 1и! (уре онегинов(т! (уре с = Тг::еоТ() ); //область записи заиолиеиа )' Функции ипь!ег17ои() и и17ои() вызываются для того, чтобы получить символ нз реального источника, когда буфер пуст.