Страница 19 из 25 Ввод и вывод С не предусматривает каких-либо возможностей по поддержке ввода/вывода. Традиционно программист опирается на библиотечные функции типа 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 ( ) также доступны для потоков ввода/вывода. |