Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 158
Текст из файла (страница 158)
|гЫй (Ь~'.~. ый); з « Ь!. га!; У составляем строку в я ге|иги оз «з.яи (); (т выводим я в оя ) Написание менее примитивной реализации операции «я оставляю в качестве упражнения 521.10[21)). Классы готт и Воипй уогт легко расширяются для форматирования целых чисел, строк типа зяг(пВ и т.д.
(см. 521.10(20)). Отметим, что представленные обьявления превращают комбинацию операций «и () в тернарную операцию; сои!«зс14(4) собирает оя|геат, формат и значение в единую функцию до того, как выполнить какое-либо реальное вычисление. 21.5. Файловые и строковые потоки 21.5. файловые и строковые потоки Когда программа на С++ запускается на выполнение, потоки соиг, сегг, с1оя, с)и и их «широкие» аналоги 521.2.1) уже готовы к применению.
Они доступны по умолчанию, а их связь с устройствами ввода/вывода определяется «системой», Дополнительно, вы можете создать собственные потоки. В этом случае вы должны указать, к чему эти потоки нужно прикрепить. Прикрепления к файлу или к строке мпла весьма типичны, так что они непосредственно поддерживаются стандартной библиотекой. Рассмотрим иерархию стандартных потоковых классов: 1ое Ьаее 1ое<> ь е оягеат<> ми еат<> 1«гг1ияеггеат< > гуеггеат<> 1оеггеат<> оТ»ггеат < > оегг1иееггеат< > ~ягеат<> егпадягеат<> Классы, оканчивающиеся на <> — это шаблоны, параметризуемые типом символов, а их имена имеют префикс Ьаяс . Пунктирная линия соответствует виртуальному базовому классу 1в15.2.4).
Файлы и строки являются примерами контейнеров, куда вы можете писать и откуда вы можете считывать данные. Следовательно, вы можете завести поток, поддерживающий как операцию «, так и операцию». Такой поток называется юятеат, он определен в пространстве имен хи1 и представлен в заголовочном файле <1ое» еаги: гетр1аге<с1а»е СЬ, с1ат Тг = сваг ггайе<СЬ» с1ат Ьаяс 1оеиеат: риЫ)с Ьаяс Йгеат<СЬ, Тг>, риЫ1с Ьаяс оягеаги<СЬ, Тг> 1 риЫ1с: ехр11<11 Ьае)с 1оягеат )Ьаяс ягеатЬиу<СЬ, Тг>* »Ь); ига«1 -Ьаяс 1огггеат (); )' 1урейе/Ьае1с 1оюгеат<сваг> 1оеиеат; гурегтеТЬаяс 1оягеат<тсааг г> ьиоягеат; Чтение из и запись в юетгеат контролируется операциями «взять из буфера» и «положить в буфер», выполняемыми над буфером еггеатьиг потока 1оеегеат (в21.б.4).
Глава 2). Потоки 752 21.5.1. Файловые потоки Рассмотрим программу, которая копирует один файл в другой. Имена файлов задаются аргументами командной строки: )У файчовые потоки и операции УУ соч, соий сего и т д. У) сх((!), и т.д. №чпс!ич(е <Тяггеат > №1пс!ия(е <!отгеат> №(пс1ийе <сяз~йй> соЫ епог(сопя! сйаг* р, сопя! сйаг* р2 = "" ) ( зМ::сегг«р« ' ' «р2« 'ч,п'з зи1:: ехй (1); ) т! та!п (ш! агдс, сйаг* агяг [ ) ) ( (Т(агдс ! = 3) еггог("чггопя питЬег оТагаитеп!я" ) зМч ч чззггеат )гот (агяг [1) ) ' чУ открываем файчовыи поток на вход з1 (!)гот) еггог("саппог орел !при!уз!в", агдг[1) ) з зи(: ч оуяггеат (о (агав [2) ) з У открываем файловый поток но выход (Т(! го) еггог ("сипло! орел ои(риг [з(е", агвг [2) ) ) сйаг сй; чгй!!в[Ггопч.ае!(сй) ) !о.риз(сй); Укопируем символы чу ( ))гот . еоТ( ) [ ( ! го ) еггог ( "яотеИипя я(гапке йаррепей" ) ч Ьаис!)1еЬиТ<СЬ, Тг>* пйиТ() сопя(; УУ получить указатель на текущий УУ файчовый буфер 62! 6.4) Ьоо1Ы ореп () сопя!; гоЫ орел (сопя! сйаг* р, орептог(е т = оил гоЫ с1озе (); )' Класс Ьаз1с !1я(геат похож на Ьаз[с о)ятгеат, за исключением того, что является производным от Ьаз1с тятеат и по умолчанию открыт для чтения.
Дополнительно, стандартная библиотека предлагает класс Ьаяс /зггеат, который похож на Ьая[с о1"- Файл открывается для ввода созданием объекта класса !Тяггеат [входной файловый поток) с именем класса в качестве аргумента. Подобным же образом файл для вывода открывается созданием объекта класса огзггеазп [выходной файловый поток) с именем класса в качестве аргумента. В обоих случаях, мы проверяем состояние созданных объектов, чтобы удостовериться в успешном открытии файлов. Класс Ьаз1с о1тгеат определяется в заголовочном файле <1яггеат>: !етр!аге<с1азя Сй, с1азз Тг = сйаг ггайз<СЬ» с!азз Ьаас огя(геат: риЫ(с Ьаяс оя1геат<Сй, Тг> ( риЫ1с: Ьая!с о)япеат (); ехрйсй Ьазчс оЯгеат ( сопя! сйаг* р, орептойе т = оип; 21 5 Файловые и строковые потоки 753 яиеази за исключением того, что он наследуется от Ьаз1с 1ояягеат и по умолчанию открыт как на чтение, так и на запись.
Как обычно, с помощью операторов ореаеу'определяются наиболее распространенные типы: яурейе/ Ьазйз 1/яягеат<сйаг> (гяягеат; Яуреае/Ьая/с оуяягеат<сйаг> оуяягеат 1 гурееву Ьаис1яягеат <сдпг> уяягеат; Яуреаеу" Ьая!с зуяягеат<всйаг 1> в(тзягеатз яуреаеГЬая/с о)яягеат<всйаг 1> вотя1геат; Яуреаеу' Ьая/с ~яягеат<всйаг 1> вуяягеат з Второй аргумент конструкторов классовых потоков определяет альтернативные режимы открытия; с1аяя 1оя Ьаяе ( риЫ/с: // ... Гуреев/' 1тр1етеп1аиои аерпеаЗ орептоае( Действительные значения для полей типа орептоз1е и их смысл зависят от реализации.
Проконсультируйтесь со справочником по вашей реализации и поэкспериментируйте. Комментарии также помогают определить основное назначение режимов. Например, мы можем открыть файл для добавления данных в конец файла: о)яягеат туяггеат (пате.с я1г [), зояйазе::арр); Также можно открыть файл для чтения и записи. Например: уяягеат а/с11опагу("сопсогйапсе", 1оя Ьаяе::1п ) 1оя Ьаяе::оия) з 21.5.2. Закрытие потоков Файл можно явным образом закрыть, вызвав функцию с1ояе () для его потока: Неявным образом это делается деструктором потока. Поэтому явный вызов функции с1озе () нужен лишь тогда, когда мы хотим закрыть файл до выхода из области видимости, в которой поток объявлен.
Майе орептоае арр, аге, Ьз пазу, из, оия, пипсз году) (отгеать туяягеат) ( // .. туяягеат. с1ояе () 1 ) // добавление в конец // открыть и перейти к концу файла // 1/О в бинарном режиме (а не текстовом) //открыть но чтение //открыть но запись //урезать файл до нулевой длингл Глава 21. Потоки В связи с этим возникает вопрос, как реализации могут гарантировать создание предопределенных потоков соиг, с(л, сеют и с1ои до их первого использования и закрытие этих потоков строго после их последнего использования? Ясно, что разные реализации <1овюгеат> с этой целью могут использовать разные приемы.
Более того, детали реализации должны быть скрыты от пользователя. Здесь я просто приведу один достаточно общий прием, гарантирующий правильный порядок создания и уничтожения глобальных объектов различных типов. Коммерческие реализации могут эту задачу решить эффективнее за счет применения специфических черт конкретных компиляторов и компоновшиков.
Основная идея состоит в том, чтобы определйть вспомогательный класс, подсчитывающий, сколько раз заголовочный файл <1овюгеат> был включен в отдельно компилируемые исходные файлы: с1ат юов Ьаве::1ий ( вюаю(с 1лю соииюю ривг(с ю 1тю (); -1ий() ю )ю иатеврасе (гов Ьаве::1ий юоютюю ) Ув <!овю еат> ,(ю в некоторый .с-файч 1ию сов Ьаве::1л!ю::соил( = Ою Кажлая единица трансляции (59.1) объявляет свой собственный объект с именем 1о1л11. Конструктор объекта 1о1и11 использует(аз Ьаве::1л(г:: соилгкак флаг первого вызова, чтобы гарантировать однократную инициализацию глобальных объектов потоковой библиотеки ввода/вывода: 1ов Ьаве::1тю::1тю() (ю1 (саит+о = О) ( /*инициализация соню сеп;стеюс.*/ ) Аналогично, деструктор объекта 1о(и11 использует 1ов Ьиге::Ьпг::салаг как флаг последнего вызова, чтобы гарантировать закрытие потоков; !ов Ьаве::1тю:: -1тю() ( (/(--соил( == О) ( 1*очистка сои(Г/)ивл,и т.д),сеп;ст и юп.д.
*/ ) ) Это общая техника программирования для работы с библиотеками, требующими инициализации и очистки глобальных объектов. В системах, где весь код во время исполнения находится в оперативной памяти, рассмотренный прием практически не имеет ограничений. В противном случае загрузка всех объектных файлов в память для выполнения их инициализирующих функций может оказаться слишком накладной.
В общем случае лучше избегать глобальных объектов. Для класса, где каждая операция выполняет значительный объем работы, лучше именно в них тестировать флаг первого вызова (вроде юов Ьаве::1л11:: соилю) для гарантирования инициализации. В то же время для потоков такой подход слишком дорог — суммарные накладные расходы на тестирование флага первого вызова в каждой операции чтения/записи одного символа будут чрезмерно велики. 2) .5. Файловые и строковые потоки 755 21.5.3.