Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 86 87 88 [ 89 ] 90 91 92 ... 120


in=1.

out=2.

ate=4.

app=010.

trunc=020.

nocreate=040.

noreplace=0100

if (!from.eof() to.badO)

error( something strange happened ); return 0;

Для открытия выходного файла создается объект класса ofstream - выходной поток файла, использующий в качестве аргумента имя файла. Аналогично, для открытия входного файла создается объект класса ifstream - входной файловый поток, также использующий в качестве аргумента имя файла. В обоих случаях следует проверить состояние созданного объекта, чтобы убедиться в успешном открытии файла, а если это не так, операции завершатся не успешно, но корректно.

По умолчанию ifstream всегда открывается на чтение, а ofstream открывается на запись. В ostream и в istream можно использовать необязательный второй аргумент, указывающий иные режимы открытия:

class ios { public: ...

enum open mode {

открыть на чтение открыть как выходной

открыть и переместиться в конец файла добавить

сократить файл до нулевой длины неудача, если файл не существует неудача, если файл существует

...

Настоящие значения для open mode и их смысл вероятно будут зависеть от реализации. Будьте добры, за деталями обратитесь к руководству по вашей библиотеке или экспериментируйте. Приведенные комментарии могут прояснить их назначение. Например, можно открыть файл с условием, что операция открытия не выполнится, если файл уже не существует:

void f()

ofstream mystream(name,ios::outios::nocreate); if (ofstream.badO) { ...

...

Также можно открыть файл сразу на чтение и запись:

fstream dictionary( concordance , ios::inios::out);

Все операции, допустимые для ostream и ostream, можно применять к fstream. На самом деле, класс fstream является производным от iostream, который является, в свою очередь, производным от istream и ostream. Причина, по которой информация по буферизации и форматированию для ostream и istream находится в виртуальном базовом классе ios, в том, чтобы заставить действовать всю эту последовательность производных классов. По этой же причине операции позиционирования в istream и ostream имеют разные имена - seekp() и seekg(). В iostream есть отдельные позиции для чтения и записи.

10.5.1 Закрытие потоков

Файл может быть закрыт явно, если вызвать close() для его потока:

mystream.closeO;

Но это неявно делает деструктор потока, так что явный вызов close() может понадобиться, если только файл нужно закрыть до достижения конца области определенности потока.



Здесь возникает вопрос, как реализация может обеспечить создание предопределенных потоков cout, cin и cerr до их первого использования и закрытие их только после последнего использования. Конечно, разные реализации библиотеки потоков из <iostream.h> могут по-разному решать эту задачу. В конце концов, решение - это прерогатива реализации, и оно должно быть скрыто от пользователя. Здесь приводится только один способ, примененный только в одной реализации, но он достаточно общий, чтобы гарантировать правильный порядок создания и уничтожения глобальных объектов различных типов.

Основная идея в том, чтобы определить вспомогательный класс, который по сути служит счетчиком, следящим за тем, сколько раз <iostream.h> был включен в раздельно компилировавшиеся программные файлы:

class Io init {

static int count;

...

public:

Io init();

Io init();

static Io init io init ;

Для каждого программного файла определен свой объект с именем io init. Конструктор для объектов io init использует Io init::count как первый признак того, что действительная инициализация глобальных объектов потоковой библиотеки ввода-вывода сделана в точности один раз:

Io init::Io init()

if (count++ == 0) {

инициализировать cout инициализировать cerr инициализировать cin и т.д.

Обратно, деструктор для объектов io init использует Io count, как последнее указание на то, что все потоки закрыты:

Io init::Io init()

if (--count == 0) {

очистить cout ( сброс, и т. д.) очистить cerr ( сброс, и т. д.) очистить cin и т. д.

Это общий прием работы с библиотеками, требующими инициализации и удаления глобальных объектов. Впервые в С++ его применил Д. Шварц. В системах, где при выполнении все программы размещаются в основной памяти, для этого приема нет помех. Если это не так, то накладные расходы, связанные с вызовом в память каждого программного файла для выполнения функций инициализации, будут заметны. Как всегда, лучше, по возможности, избегать глобальных объектов. Для классов, в которых каждая операция значительна по объему выполняемой работы, чтобы гарантировать инициализацию, было бы разумно проверять такие первые признаки (наподобие Io init::count) при каждой операции. Однако, для потоков такой подход был бы излишне расточительным.

1 0.5.2 Строковые потоки

Как было показано, поток может быть привязан к файлу, т.е. массиву символов, хранящемуся не в основной памяти, а, например, на диске. Точно так же поток можно привязать к массиву символов в



основной памяти. Например, можно воспользоваться выходным строковым потоком ostrstream для форматирования сообщений, не подлежащих немедленной печати:

char* p = new char[message size]; ostrstream ost(p,message size); do something(arguments,ost); display(p);

С помощью стандартных операций вывода функция do something может писать в поток ost, передавать ost подчиняющимся ей функциям и т.п. Контроль переполнения не нужен, поскольку ost знает свой размер и при заполнении перейдет в состояние, определяемое fail(). Затем функция display может послать сообщение в настоящий выходной поток. Такой прием наиболее подходит в тех случаях, когда окончательная операция вывода предназначена для записи на более сложное устройство, чем традиционное, ориентированное на последовательность строк, выводное устройство. Например, текст из ost может быть помещен в фиксированную область на экране.

Аналогично, istrstream является вводным строковым потоком, читающим из последовательности символов, заканчивающейся нулем:

void word per line(char v[], int sz)

печатать v размером sz по одному слову в строке

istrstream ist(v,sz); создать istream для v char b2[MAX]; длиннее самого длинного слова

while (ist>>b2) cout <<b2 << \n ;

Завершающий нуль считается концом файла. Строковые потоки описаны в файле <strstream.h>.

10.5.3 Буферизация

Все операции ввода-вывода были определены без всякой связи с типом файла, но нельзя одинаково работать со всеми устройствами без учета алгоритма буферизации. Очевидно, что потоку ostream, привязанному к строке символов, нужен не такой буфер, как ostream, привязанному к файлу. Такие вопросы решаются созданием во время инициализации разных буферов для потоков разных типов. Но существует только один набор операций над этими типами буферов, поэтому в ostream нет функций, код которых учитывает различие буферов. Однако, функции, следящие за переполнением и обращением к пустому буферу, являются виртуальными. Это хороший пример применения виртуальных функций для единообразной работы с эквивалентными логически, но различно реализованными структурами, и они вполне справляются с требуемыми алгоритмами буферизации. Описание буфера потока в файле <iostream.h> может выглядеть следующим образом:

class streambuf { управление буфером потока

protected:

char* base; начало буфера

char* pptr; следующий свободный байт

char* gptr; следующий заполненный байт

char* eptr; один из указателей на конец буфера

char alloc; буфер, размещенный с помощью new

...

Опустошить буфер:

Вернуть EOF при ошибке, 0 - удача virtual int overflow(int c = EOF); Заполнить буфер:

Вернуть EOF в случае ошибки или конца входного потока, иначе вернуть очередной символ virtual int underflow(); ...



1 ... 86 87 88 [ 89 ] 90 91 92 ... 120

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика