Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 155
Текст из файла (страница 155)
Аналогично, с<и . ехсерполз (юз Ьазе «Ьа«Ы< ( <оз Ьазе <:/аИЫ<)," позволит перехватить не слишком редкий случай, когда на входе присутствуют данные не в ожидаемом формате и операция ввода не возвращает никаких значений из потока. Вызов ехсерпопз() без аргументов возвращает набор флагов состояния ввода/вывода, инициировавшего исключение. Например: «оЫрпл< ехсерполз(<оз Ьазеь юз) ( юз Ьазе::(оз<а<е з = <оз.ехсерполз() < (Т(зяюз вазе::ЬайЫ<) сош « "<Ьго<вз/ог Ьаат< (1(зяюз Ьазе <:/аИЬ<1) сои< « "<Ьго<«з /ог /аИ" < 11 (яшоз Ьазе::ео7Ы<) сош « "<Ьго<гз/ог ео/" 1 (Т(з == 0) сои< « "<<вези '< <Ьго<«"; ) Главная цель исключений ввода/вывода — отлавливать наименее вероятные ошибки, о которых часто забывают. Другая цель — управлять вводом/выводом. Например: го<«геасдл<з (гес<ог<ш<)я з) ( <оз Ьазе:: юз<а<е оЫ <аа<е = с!п.
ехсерпоиз () < У сохраняем состояние исключение сш. ехсерпопз Ноя Ьазе:: ео/Ы<) < /< генерируем исключение для ео/ /ог(1<) пу ( <и< 11 с1п»1< з.ризЬ Ьасй(1) < ) са<сЬ (<оз вазе::/аписе) << о!с достигнут конец файла ( Ьгеай< ) с<л.ехсер<юлз(оЫ з1а1е) < Увосстанавливаем состояние исключения ) Вопросы, задаваемые в связи с этим таковы: «Разве это ошибка?» или «Разве это действительно исключительная ситуация?» (Э)4.5).
Я считаю, что ответ на оба этих вопроса — «нет». И поэтому я предпочитаю работать с состоянием потока напрямую. То, что легко обрабатывается локальными управляющими структурами внутри функций, редко когда нуждается в использовании исключений. 21.3. Ввод 737 21.3.7.
Связывание потоков Функция ле ( ) из Ьаз(с (оз используется для того, чтобы устанавливать и разрывать связи между (з(геат и азл еат: (етр(а(е<с(азз Сй, с(азз Тг = сйаг сга(ь<СЬ» с(азз зи(::Ьаз(с (оз: риЫ(с!оз Ьазе ( й... Ьаз!с оз(геат<СЬ, Т«>* «е() солт; ~уполунаем указатель на связанный поток Ьаз(с отгеат<Сй, Тг>* Ве(йаз(с озггеат<СЬ, Тг>* з) 1 уприаязмваем *(й(з к з У.. )' Рассмотрим следующий код: з(г(ле ее( раззнн(() ( з(г(ле з; сои( « "Раззноп1: сйа» ю У..
) Как мы можем быть уверены в том, что надпись Раззлог((: появится на экране до того, как завершится операция чтения? Вывод в сои( буферизуется, так что если бы с(л и сои( были независимыми, то надпись Раззаогг(: не появилась бы на экране до тех пор, пока буфер полностью не заполнился бы.
Решение этой проблемы состоит в том, что поток сои( связывается с потоком с(л операцией с(л.((е(зсои() . Когда озггеат связан с (з(геат, поток оз(геаш очищается каждый раз, когда операция ввода над м(геат испытывает недостаток символов, то есть когда требуются символы из конечного источника ввода для завершения операции ввода. Таким образом, сои( « "Раззвог((." с(л » 5" эквивалентно сои( « РаззЯО«г(: сои(.7(изй ( ); ст» з; Поток ввода может иметь не более одного связанного с ним оз(геат. Вызов з. пе(0) отвязывает поток з от потока, с которым он был связан (если был связан). Как и большинство других поточных функций, устанавливающих значение, функция 1(е (з) возвращает прежнее значение, то есть она возвращает предыдущий связанный поток или О.
При вызове без аргумента функция ле() возвращает текущее значение без его изменения. Из стандартных потоков сои( привязывается к с(л, а (гсои( — к (гс(л. Потоки сегг не нуждаются в привязке, поскольку они не буферизованы, а потоки с!оя не предназначены для взаимодействия с пользователем. 7ЗВ Глава 21. Потоки 21.3.8. Часовые (ве(т1г(ев) гетр)аге<с(аяя СЬ, с(аяз Тг = слог ггайя<СЬ» с(ат Ьаис ояггеат: г!гяиа!риЫ(с Ьаас 1оя<СЬ, Тг> ( //...
с1ат яеппу; // ... )' гетр!иге<с(ат СЬ, с!от Тг = слог ггаия<СЬ» с!от Ьая/с отгеат<СЬ, Тг>:: зепау ( риЫ(с: ехрйсй яеппу (Ьаяс озггеат<СЬ, Тг> з я); -яепау (); орегаяог Ьоо((); // ... )' Таким способом вычленяется общий код и остальные функции пишутся следуюшим образом: гетр(а!в<с(аяя СЬ, с!вяз Тг = слог ггайя<СЬ» Ьаяс озггеат<СЬ, Тг>ь Ьаис отгеат<СЬ, Тг>::орега1ог«(гпг 1) ( яепиу я (*Й(я); (Т(!я) ( яегязаГе (Та(1Ы1) ге!ига *Йй; ) // проверка готовности к началу вывода // выводим (п( ге!ига *ЙВ; Когда я писал операции «и» длл типа сотр!ех, я не беспокоился о связывании потоков (521.3.7) и не думал о том, генерируются ли исключения при изменении состояний потоков Ц21.3.6). Я предполагал (совершенно справедливо), что библиотечные функции сами об этом позаботятся.
Но как? Всего таких функций несколько десятков, и если бы нам самим пришлось писать нетривиальный код для управления связанными потоками, локализацией (521.7), исключениями и т.д., наш код стал бы слишком сложным и запутанным. В стандартной библиотеке использован подход, предполагающий применение класса зепзгу («часовой») для реализации перечисленных выше общих задач.
Код программы, который должен исполняться первым (префикс-код) — например, сброс связанного потока — предоставляется посредством конструктора класса яеиву. А код, который должен исполняться последним (суффикс-код) — например, генерация исключений из-за изменения состояния потока — предоставляется посредством деструктора класса зепиу: 21.4.
Форматирование Рассмотренная технология предоставления префикс-кода и суффикс-кода в виде конструктора и деструктора специального класса полезна и во многих других контекстах. Само собой разумеется, что и класс Ьаз!с !зггеат располагает своим собственным «часовым»о 21.4. Форматирование Все примеры из В2!.2 относятся к так называемому неформатированному выводу, означающему, что обьект превращается в последовательность символов по умолчательным правилам.
Но часто возникает необходимость в более детализированном управлении этим процессом, например, в контроле за обьемом памяти, выделяемом для операции вывода, или в задании формата вывода чисел. Аналогично, возникает необходимость и в управлении отдельными аспектами ввода.
Управление форматированием ввода/вывода сосредоточено в классе Ьав!с !оз и в его базовом классе !аз Ьазе. Например, класс Ьаз!с юз содержит информацию о системе счисления (восьмеричная, десятичная или шестнадцатеричная), которая используется при вводе или выводе целых чисел. Он также содержит функции для установки и проверки этих управляющих переменных потока. Класс Ьаз!с юз является базовым для Ьаьтс виват и Ьатс озггеат, так что управление форматом выполняется по отдельности для каждого потока.
21.4.1. Состояние форматирования Форматирование ввода/вывода контролируется набором флагов и целочисленных значений'в классе 1оз Ьазе: с1азз юз Ьазе ( риЫс: уу ... !!имена флагов форматирования: !урез!е/!тргетеп1аяоп де/зпед!/тфаязз гаапс сопл(/тфадз з)арнп, УУ пропускать пробельные символы на входе 1е/1, У)выравнивание поля: заполнитель после значения г!яйг, УУ заполнитель перед значением 1пгегпа1, У заполнитель между знаком и значением Ьоо1а!рйа, УУ использовать символьное представление для ггие и/а!зе Нес, У)основание для вывода цельш: !О йех, УУ вывод по основанию !6 (шестнадцатеричный) ос1, УУ вывод по основанию 8 (восьл~еричный) зс(епз!/зс, УУ форма чисел с плавающей запятой: г!.ЙЙИЫЕЙЙ Ги ез1, УУ гйй!с!юг) зйошЬазе, У(префикс 0 для восьмеричных и 0х - для шестнадцатеричных зйоизро(пз, УУ печатать хвостовые (незначащие) ну«и зйошроз, Уявньш" '+' для положительных!пз иррегсазе,,У 'Е', РР а не 'е', 'х' а4изЯе!д, УУ флаги для выравнивании полей (з2!.4.5) Глава 2П Потоки 740 базе/)еЫ, // флаги системы счисления целых 521.4.2) Яоа(ГьеЫ, //флаги вывода чисел с плов.
запятой Я2!.4.3) итгьвуг //очистка буфера после каждой операции вывода // читать флаги //установить флаги Гт())аВяфаяз () сопля; Гтг/(аде )Галя ~/тг))аав Я; )т())аяяяег)~тфаяяя (гегитпЯабз (у)аяз () (/); ) /удобавить флаг Гт())аяз зег~~тгу)адзЯ Гтфаяя таей) (гегигп Яаяз ( фаВз ( ) я-таей) ( ()ипаяй) ); ) коЫ ипзе(Гфигт)адя таза) ())аВз ())аВя () я-таза); ) // очистить флаги /У... ); Значения флагов зависят от реализации. Используйте только их символические имена, а не соответствующие им числовые значения, даже если вы уверены за эти значения для вашей конкретной реализации. сопят!оз Ьаяе: )т9ау ту орг = /оя базе::!еГг(Ыз Ьазе::осг(Ыя Ьаяе::))хе»(; Предоставление интерфейса управления в виде набора флагов вкупе с функциями для их установки и сброса хоть и выглядит несколько старомодно, зато проверено временем. Главное достоинство такого подхода заключается в том, что пользователь может легко комбинировать имеющиеся опции.
Например: коЫ уоие ~ипс6оп Доя Ьаяе::Гтфаяз орг) ( Гоз Ьазе::Гтг)тадя оЫ орйопз = соиГ.Яадз (орй; //установтпь новые //и сохранить старые опции // ... соиг .))аяя ( оЫ орпопз ) ) // восстановить старые опции коЫ ту ~ипспоп ( ) ( уоие ~ипсг)оп (ту орг) // ... ) Функция Яау() возвращает старый набор флагов (опций). Имея возможность читать и задавать любые опции, мы можем устанавливать значения отдельных флагов.
Например: туоягееат.)(аяз (туояггеат.Яаяз () ()оя ьазе::зьоыроз) что заставляет поток шуоя(геаш явно показывать знак» перед положительными числами, в то время как остальные опции остаются без изменений. Здесь мы прочли старый набор опций и с помощью логической операции «побитовое ИЛИ» внесли в него зйопроз. Функция зер() делает то же самое, так что предыдущий пример с ее помощью записывается короче: туозггеат . яев" ((оз Ьазе:: явоыроз); После установки флаги сохраняют свое состояние до тех пор, пока не будут сброшены. Управление опциями ввода/вывода путем явной установки и сброса флагов сложно, утомительно и чревато ошибками.