Бьерн Страуструп - Абстракция данных в языке С++ - Ввод и вывод
ОГЛАВЛЕНИЕ
Ввод и вывод
С не предусматривает каких-либо возможностей по поддержке
ввода/вывода. Традиционно программист опирается на библиотечные
функции типа p r i n t f ( ) и s c a n f ( ) . Например, для
того, чтобы напечатать структуру данных, представляющую
комплексное число, можно записать:
printf ("(%g,%g)\n", zz.real, zz.imag);
К несчаcтью, так как в старом С стандартные функции
ввода/вывода знают только стандартные типы, необходимо печатать
структуру почленно. Зачастую это утомительно и может быть сделано
только в том случае, если члены структуры доступны. Проблема в
общем случае не может быть решена расширением поддержки
определенных пользователем типов и форматов ввода/вывода.
Подход, принятый в С++, заключается в том, чтобы
предоставить (в "стандартной" библиотеке, не в самом языке)
операцию
типа. Для данного выходного потока c o u t можно записать:
cout
Реализация класса c o m p l e x определяет
ostream & operator
return s
Операции
отдельных вызовов для каждого аргумента. Например:
put(cout, "("); /* невыносимо избыточно */
put(cout,c.real);
put(cout
put(cout, ")\n");
По сравнению с p r i n t f , имеется потеря управления по
форматированию вывода.В том случае, если необходимо более тонкое
управление, можно использовать "функции форматирования".
Например:
cout
представление своего первого аргумента.
для типа данных i s t r e a m для каждого базового и
определенного пользователем типа. Если операция ввода завершится
неуспешно, поток войдет в состояние ошибки, что приведет
последующие за ним операции к неуспешному завершению. Для
переменной z z любого типа можно написать такой, например, код:
Достойно удивления, что операции ввода обычно тривиальны для
написания, т.к. всегда имеется в наличии конструктор для
выполнения нетривиальной части работы, аргументы конструктора
(конструкторов) дают хорошее первое приближение формата ввода.
Например:
{
if (!s) return s;
double re = 0, i = 0;
char c1 = 0, c2 = 0, c3 = 0;
if {c1 != '/' || c2 !=','|| c3;
if (c1 != '(' || c3 != 1) ) s.state = _bad;
if (s) zz = complex(re, im);
return s;
}
Соглашением для функций, реализующих операции ввода/вывода,
является возвращение аргумента-потока с указанием успешного или
ошибочного завершения (состояния). Данный пример чуть-чуть
излишне прост для реального использования, но приведенная функция
изменит значение аргумента · · и оставит поток в неошибочном
состоянии тогда и только тогда, если будет обнаружено комплексное
число в форме ( d o u b l e , d o u b l e ). Интерпретация
проверки потока на неравенство нулю как проверка его состояния
обеспечивается посредством перегрузки операции ! s для
i s t r e a m . Например, вышеприведенная проверка i f ( s )
интерпретируется как i f ( б
( ) , которая, наконец, проверит s. s t a t e .
Заметим,а что не происходит потери информации о типе при
использовании
p r i n t f и s c a n f можно избежать большого класса ошибок.
Болееа того,
(определенного пользователем) типа, не затрагивая "стандартные"
классы i s t r e a m и o s t r e a m никоим образом, также
без знания об устройстве этих классов. ¦ s t r e a m может быть
связан с реальным выводным устройством, как и i s t r e a m .
Это значительно расширяет диапазон применения и избавляет от
нужды в функциях s s c a n f и s p r i n t f старого С.
Посимвольные операции p u t ( ) и g e t ( ) также
доступны для потоков ввода/вывода.