C++
Шрифт:
void word_per_line(char v[], int sz) /* печатет "v" размера «sz» по одному слову на строке */ (* istream ist(sz,v); // сделать istream для v char b2[MAX]; // больше наибольшего слова while (ist»»b2) cout «„ b2 «« «\n“; *)
Завершающий нулевой символ в этом случае интерпретируеся как символ конца файла.
В помощью ostream можно отформатировать сообщения, котрые не нужно печатать тотчас же:
char* p = new char[message_size]; ostream ost(message_size,p); do_something(arguments,ost); display(p);
Такая операция, как do_something, может писать в поток ost, передавать ost
8.6 Буферизация
При задании операций ввода/вывода мы никак не касались типов файлов, но ведь не все устройства можно рассматривать одинаково с точки зрения стратегии буферизации. Например, для ostream, подключенного к символьной строке, требуется буферзация другого вида, нежели для ostream, подключенного к фалу. С этими пробемами можно справиться, задавая различные бферные типы для разных потоков в момент инициализации (обратите внимание на три конструктора класса ostream). Есть только один набор операций над этими буферными типами, поэтму в функциях ostream нет кода, их различающего. Однако фунции, которые обрабатывают переполнение сверху и снизу, виртальные. Этого достаточно, чтобы справляться с необходимой в данное время стратегией буферизации. Это также служит хорошим примером применения виртуальных функций для того, чтобы сдлать возможной однородную обработку логически эквивалентных средств с различной реализацией. Описание буфера потока в «stream.h» выглядит так:
struct streambuf (* // управление буфером потока
char* base; // начало буфера char* pptr; // следующий свободный char char* qptr; // следующий заполненный char char* eptr; // один из концов буфера char alloc; // буфер, выделенный с помощью new
// Опустошает буфер: // Возвращает EOF при ошибке и 0 в случае успеха virtual int overflow(int c =EOF);
// Заполняет буфер
// Возвращет EOF при ошибке или конце ввода, // иначе следующий char virtual int underflow;
int snextc // берет следующий char (* return (++qptr==pptr) ? underflow : *qptr amp;0377; *)
// ...
int allocate //выделяет некоторое пространство буфера
streambuf (* /* ... */*) streambuf(char* p, int l) (* /* ... */*) ~streambuf (* /* ... */*) *);
Обратите внимание, что здесь определяются указатели, нобходимые для работы с буфером, поэтому обычные посимвольные действия можно определить (только один раз) в виде максимално эффективных inlinфункций. Для каждой конкретной стратгии буферизации необходимо определять только функции перепонения overflow и underflow. Например:
struct filebuf : public streambuf (*
int fd; // дескриптор файла char opened; // файл открыт
int overflow(int c =EOF); int underflow;
// ...
// Открывает файл: // если не срабатывает, то возвращет 0, // в случае успеха возвращает «this» filebuf* open(char *name, open_mode om); int close (* /* ... */ *)
filebuf (* opened = 0; *) filebuf(int nfd) (* /* ... */ *) filebuf(int nfd, char* p, int l) : (p,l) (* /*...*/ *) ~filebuf (* close; *) *);
int filebuf::underflow // заполняет буфер из fd (* if (!opened !! allocate==EOF) return EOF;
int count = read(fd, base, eptr-base); if (count « 1) return EOF;
qptr = base; pptr = base + count; return *qptr amp; 0377; *)
8.7 Эффективность
Можно было бы ожидать, что раз ввод/вывод «stream.h» определен с помощью общедоступных средств языка, он будет мнее эффективен, чем встроенное средство. На самом деле это не так. Для действий вроде «поместить символ в поток» использются inline-функции, единственные необходимые на этом уровне вызовы функций возникают из-за переполнения сверху и снизу.
Для простых объектов (целое, строка и т.п.) требуется по оному вызову на каждый. Как выясняется, это не отличается от прочих средств ввода/вывода, работающих с объектами на этом уровне.
8.8 Упражнения
1. (*1.5) Считайте файл чисел с плавающей точкой, составьте из пар считанных чисел комплексные числа и выведите комплексные числа.
2. (*1.5) Определите тип name_and_address (имя_и_адрес). Определите для него «„ и “». Скопируйте поток объектов name_and_address.
3. (*2) Постройте несколько функций для запроса и чтения различного вида информации. Простейший пример – функция y_or_n в #8.4.4. Идеи: целое, число с плавающей токой, имя файла, почтовый адрес, дата, личные данные и т. д. Постарайтесь сделать их защищенными от дурака.
4. (*1.5) Напишите программу, которая печатает (1) все бувы в нижнем регистре, (2) все буквы, (3) все буквы и цифры, (4) все символы, которые могут встречаться в идентификаторах С++ на вашей системе, (5) все символы пунктуации, (6) целые значения всех управляющих симвлов, (7) все символы пропуска, (8) целые значения всех символов пропуска, и (9) все печатаемые символы.
5. (*4) Реализуйте стандартную библиотеку ввода/вывода C («stdio.h») с помощью стандартной библиотеки ввода/вывда С++ («stream.h»).
6. (*4) Реализуйте стандартную библиотеку ввода/вывода С++ («stream.h») с помощью стандартной библиотеки ввода/ввода C («stdio.h»).
7. (*4) Реализуйте стандартные библиотеки C и С++ так, чтбы они могли использоваться одновременно.
8. (*2) Реализуйте класс, для которого [] перегружено для реализации случайного чтения символов из файла.
9. (*3) Как Упражнение 8, только сделайте, чтобы [] работло и для чтения, и для записи. Подсказка: сделайте, чтбы [] возвращало объект «дескрипторного типа», для котрого присваивание означало бы присвоить файлу через дескриптор, а неявное преобразование в char означало бы чтение из файла через дескриптор.