Бьерн Страуструп - Абстракция данных в языке С++ - Ввод и вывод

ОГЛАВЛЕНИЕ

 

        Ввод и вывод

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