Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 157
Текст из файла (страница 157)
17изЬ () вызывается в необходимом порядке. Для этого имеется соответствующая версия операции «, которая принимает указатель на функцию и вызывает функцию по этому указателю: >етр1а>е<с>ат СЬ, с1ая Тг = сваг >гойя< СЬ» с!ая Ьаяс озйеат: >Ягтиа(риЫ>с Ьаяс >оз<СЬ, Тг> ( риЫ>с: >'.. Ьаис оягеа>ня орегагог«(Ьаяс оз>геатз ('1) (Ьаяс озйеаи>Ь) ) (ге1иги1(*>ЬЫ) >) Ьаяс оз1геатз орегагог«(1оз Ьазеь ("1) (Ыз Ьазез) ); Ьаяс озйеа>из орегаии«(Ьазк 1оз<СЬ, Тг>а ("1) (Ьаяс >оз<СЬ, Тг>ь) ) > >(> ...
Чтобы это работало, функция должна быть или статической функцией-членом, или глобальной функцией правильного типа. В частности, функция11изЬ() определяется следующим образом: 1етр!а1е<с(ат СЬ, с1азз Тг = сваг >га>>з<СЬ» вазы оягеат<СЬ, Тг>я 1)изй (Ьаз>с оягеат<СЬ, Тг>з з) ( гей>ги з.11изл (); // вязов1(изЬ() - функции-члена озтеот Это объявление гарантирует, что выражение сои> «1)изЬ > разрешается как вызов сои>. орегаыг« ())изЬ ) 7Я6 Глава 21.
Потоки приводящий к вызову 7)ивй (сои!) с который, в свою очередь, вызывает сои!.у)ивй ( ) Все это делается (на стадии компиляции) ради того, чтобы вызов Ьаяс овсгеат::усивй () порождался из внешне простого выражения сои!«7)сий. Существует множество операций, которые нам хотелось бы выполнять непосредственно перед или сразу после операции ввода или вывода. Например: соис «хс сои!.угивй ( ); сои! «ус ссп. иове(Г(!ов Ьаве::вйрюв); с!п»х; УУ не пропускать пробельные символы 621.4.!) сои! «х «С!ивй «у; ссп» повЫрпв» хс Заметьте, что манипуляторы определены в пространстве имен всс1, так что их нужно в общем случае квалифицировать префиксом всс1:: (если это не входит в текущую область видимости): всь(:: сои! «епс(1; вс4:: сои! «впс;; епс!1; УУ ег ои епс!! - вне области видимости У о(с Естественно, класс Ьаяс !всгеасп предоставляет операции», способные активизировать манипуляторы так же, как и рассмотренный выше класс Ьаяс овсгеапк гетр!асе<с1авв Сй, с1аи Тг = сйаг сга!и<СЬ» с(аи Ьаяс исгеат: игсиасриЫ!с Ьаяс сов<Сй, Тг> ( риЫ!с: ,Е.
Ьаяс !всгеать орегасог» (Ьав(с исгеать ('ру) (Ьаяс ипеать) ); Ьавсс исгеать орегасог» (Ьаяс сов<СЬ, Тг>ь ("ру) (Ьаяс сов<Си, Тг>ь) ) с Ьав!с !в(геатЬ орегаеог» ((ОВ Ьавеь (*ф) (!ов Ьавеь) ); УУ ... )' 21.Я.6.1. Манипуляторы, принимающие аргументы Манипуляторы, принимающие аргументы, также полезны. Например, мы могли бы написать; сои! «весргес1яоп (4) «апя1ес чтобы вывести значение переменной ипа1е (имеющей тип чисел с плавающей запятой) из 4 цифр. Когда операции разносятся по разным операторам, логические связи между ними становятся менее очевидными.
А потеря логической ясности приводит к трудностям в понимании кода. Тут нам и приходят на помощь манипуляторы, которые позволяют вставлять операции типа 71иьй() или ипвесу(!ов Ьаве::вй!рсгь) в компактный список (последовательность) операций ввода и вывода. Например: 21.4. Форматирование 747 Для этого зе<ргес!и!опО возвращает объект, инициализированный числом 4, и который при обращении к нему вызывает сов<.ргес<з!оп(4) . Такой манипулятор есть объект функционального класса, который активируется операцией «, а не операцией () . Точный тип этого объекта зависит от реализации, но его можно определить следующим образом; тгис< итап<р ( <ои Ьаиеь ("Я (<оз Ьаиви, <пд < Уфункция для вызова !п« < итатр(юи Ьазва ("/)<) (!ои Ьазви, <пд, т<й): з(ф), <(й) () <втр1а<в<с1аиз Сй, с1аиз Тг> Ьаз!с оз<гват<СЬ, Тг>и орвгайог«(Ьаяс отгеат<СЬ, Тг>и оз, сони< итап!ра т) ( т.з (Ои <и.<); ге<игл оз; Конструктор класса зтап!р сохраняет свои аргументы в полях /и <, а орега<ог«() вызывает/(!) .
Теперь можно определить зе<ргес!и!оп() следующим обра- У вспомогательная функция <З вызов функции-члена !оз Ьаиеи ив< ргес<яоп (юз Ьаива и, ю< и) ( з.ргес<з!оп (и); ге<ига т !и<те зтап<р ив<ргвс<зйоп (<п< и) ( го<ига зтатр (ив<ргвс!з!оп, и ) < ) <У создаем объект-функцию и использовать его: сои«< ие<ргес!з!оп (4) «апа1е; При необходимости программист может определять новые манипуляторы в стиле зтап!р (й21.101221).
При этом не требуется вносить изменения в определения шаблонов и классов стандартной библиотеки. <ои Ьаиеа Ьоо1а<рйа (1ои Ьазви); 11 символьное представление <гие и/а!ие !оз Ьаиви поЬоо!а!рйа (юи Ьаиеь и); дз.ипие</(!оз Ьаиекйоо1а!рйа) 21.4.6.2. Стандартные манипуляторы ввода/вывода Стандартная библиотека предоставляет манипуляторы, соответствующие различным состояниям форматирования и их изменениям. Стандартные манипуляторы определены в пространстве имен з<4. Манипуляторы, использующие юи Ьазе, представлены в заголовочном файле <юи>.
Манипуляторы, использующие !з<геат и озггеат, представлены в <!пгеат> и <оз<геат>, соответственно, и, кроме того, в <!озиеат>. Остальные стандартные манипуляторы представлены в «отап<р>. 748 Глава 21. Потоки !оз Ьаяеь заонбазе ((офгаяеь ); //оп оитрш ртах ос( Ьу О апг( Иех Ьу Ох юя Ьаяеь пояаонЬазе(юя Ьазеь з); //и.илзе!)((оз Ьаяе::яйонЬазе) 1оз Ьаяеь звонро!и((!оя Ьазеь; !оя Ьазеь пояИонрот((!оз Ьазеь я) ( //и.ипиеЯюи Ьоие: йонройл) !оя Ьаяеь звонроя (юз Ьазеь) ( юя Ьаяеь лоявонроз((оз Ьазеь з); /!аипие((((оз Ьазе::йонроз) !оз Ьаяеь яЫрня(юя Ьаяеь); !оз Ьаяеь позЫрнз (1оз Ьаяеь з); д пропускать пробельные символы д я.ипяеЯ(оз Ьаяе::й(рнз) !оз Ьаяеь иррегсазе(юя Ьаяеь); !оя Ьазеь поиррегсаяе ((оз Ьаяеь ) ( дХиЕ,анехие дхие,анеХиЕ (оз Ьаяеь (п(егпа(((оя Ьаяеь); юя Ьазеь (еу(((оя Ьазеь) ( юз Ьазеь т(8И((!оз Ьаяеь) ( д выравнивание 32!.4.5 // заполнитель после значение // заполнитель перед значением !оя Ьаяеь вес(юя Ьазеь); !оз Ьазеь Иех(!оз Ьаяеь); !оз Ьазеь ос((юи Ьазеь); д целые по основанию (О Я2!.4.2) д по основанию !б // по основанию 8 д формат чисел с плавающей запятой: (оя Ьазеь ()хег((!оз Ьаяеь); // формат йЫдхЫ 62!.4.3) !оя Ьояеь яс(ел(!))с((оз Ьазеь) ( дформат й.г(г(гй(ЕгЫ (етр!а(е<с(аяя СИ, с(ат Ьая(с озпеат<СИ, Тг>ь (етр1а(в<с(ат СИ, с(ат ЬазЫ оя(сеет<СИ, Тг> ь (етр!а(с<с(ат СИ, с1азя Ьатс ояггеат <СИ, Тг> ь (етр!а(е<сйт СИ, сйт Ьаяк (затеат <СИ, Тг> ь Тг> еЫ!(Ьая1с оя(геат<СИ, Тг>ь); Тт> епг(я(Ьаз!с оз(геат<СИ, Тг>ь) ( Тг> )(изИ (Ьая!с оз(геат<СИ, Тг»ь) Тт> нз(баяЫ (я(твою<СИ, Тг>ь) ( д поместит~ ')п' и )(ияЬ д поместить '10' (д очистить б)фер потока // съесть пробельные сшиволы теле((оя2(адз (!оз Ьаяе::~юпИ(адз)) яе((оя)(авя ((оз базе::Тт()(адз (); зе(Ьазе (!и! Ь); яе(((И(!п( с) ( зе(ргесЫоп (!ли и ); яе(н (1п( п); Например, саит «1234 « ', ' « Иех «1234 « ', ' «осг «1234 « епа(; выведет 1234, 4О2, 2322, а сои(« ' ( ' «зе(н (4) «зеИйИ( ' М ' ) «12 «" ) (" «12 « " ) ',и"; выведет (1() 12) (л2) .
Применяя манипуляторы без аргументов, не ставьте круглых скобок. Используя стандартные манипуляторы с аргументами, не забудьте вставить в исходный код директиву препропессора Ф(пс(и((е <ютап(р>. Например: итал ьр итал!р итал(р итал(р юпап(р юпап(р д очистить флаги 621.4) дустановить флаги 62!.4) д вывод целых по основанию Ь Я2(.4.2) //сделать с сиивололозаполнителем (32(.4.4) //л цифр Д2!.4.3, у2(.4.6) // ширина след. поля - л символов Гй2!.4.4) 749 2).4 Форматирование Ипс1ибе <гопгеагп > игбп8 патеярасе яЫ; ии та1п () ( сои! « яеяргесгтбоп (4) УУ еггогт яегргесиюп не определен (забыли <ютатр>) « яс!епя((тс() гуепог.' ояггеат«охи еатгх (неуместные скобки) «1.
414Л «еп41; ) У общий формат, точность — 4 Роет Оеп4 (4); го!а )'(аоиЫе я() ( Ропп ясг8 = 8еп4; ясг8. яс!епяа)с () .ргес!я!оп (8); УУ научный формат, точность - 8 сои! «4 « ' ' «Оеп4(И) « ' ' « яс!8(4) « ' ' «4 « ' '~п' г ) Вьгзов1(1234.5б789) выведет 1234. 57 1235 1.2345б789еь03 1234. 57 Заметьте, что использование Ропп не влияет на состояние потока, так что последний вывод числа я( имеет тот же умолчательный формат, что и первый. Вот упрощенная реализация: с1ам Воипб (огт; с1аяя Ропп ( Яепй ояягеатй орегаяог«(ояггеать, сопя! Воипб ~оста) г !пг ргс; УУ точность ии ггбяг УУ ширина тя)тгг УУ общий, научный, фиксированный Д2!.4.3) уу риЫгс г ехр!гсй гост (!пя р = б): ргс(р) )т(=О; гвбя = О; УУумолчательнал точность - б УУобщий формат Я2!.4.3) УУ ширина по необходимости 21.4.6.3.
Манипуляторы, определяемые пользователем Программист может создавать манипуляторы в стиле стандартных манипуляторов. Здесь я представлю иной (дополнительный) стиль, который может оказаться полезным при форматировании чисел с плавающей запятой. Как мы знаем, установка точности вызовом ргес!я!оп () сохраняется для всех операций вывода, а юЫ11я() применяется лишь к ближайшей операции.
Мне же хочется определить нечто такое, что упростит вывод чисел с плавающей запятой в предопределенном формате без влияния на последующие операции вывода в поток. Для этого я определю класс, представляющий форматы, а также класс, который представляет формат плюс форматируемое значение, и, наконец, функцию орегагог«() (то есть операцию «), которая выводит значение в соответствии с форматом. Например: Глава 21.
Потоки Воиий !огт арета|от() (йоиЫе 4) сопя|; Роста зс!епф1с() (!|и! = 1оя Ьазе::зс!епя!!)с; ге|игл* й|я; ) роста яхе4() (утг = 1оз Ьазе::яхе4| ге|игп *й1»| ) Роста яепега1 () (!тг = О| ге|ига *йи| ) Роста иррегсаяе(); Рост я 1о|гегсазе ( ); Когтя ргесВ!оп (|п|р) (рте = р| те|игл *й|я| ) рогтя нн41Ь(|п| |г) (и41 = и | ге|игп *1Ь!з| ) Упри»|еняется ко всем типам Рогтя у)11 (сваг); 0 явный плюс Р выводить хвостовые нули рогтя р!ия (Ьоо( Ь = и ив); роста |га(дпя »его» (Ьоо( Ь = |тие) // ...
)| Идея заключается в том, что Роет содержит всю информацию, необходимую для форматирования одного элемента данных. Выбранные умолчательные значения разумны во многих случаях, а разные функции-члены можно использовать для установки отдельных аспектов форматирования. Операция () используется для связывания значения с форматом, выбранным для его вывода. Тогда Воии4 готт можно вывести в заданный поток подходящей функцией арета|от«() (операцией «): »|гас| Воипд 1огт ( соп»1 Готт я у) 4оиЫе га1| Винти! 1огт(сопя|рогтяф, 4оиЫе г): Гщ|), га1(к) () Воипй готт Готт:: орете|от () (4оиЫе 4) сопз| ( ге|игл Воино )огт (*1Ь!»,4) | ) отгеатя арета!ог«(отгеатя оз, соп»1 Воиий /отта Ь!) ( взят!пязягеат я; У строковые потоки описаны в ях1.5.3 з.ргес!з!оп (Ья.).рте) 1 з. зео'(Ь/4'.Хт1, (оя Ьазе:: !!оаобде14); з.