Разработка и реализация стандартных средств ввода/вывода для языка программирования зарекомендовала себя как заведомо трудная работа. Традиционно средства ввода/вывода разрабатвались исключительно для небольшого числа встроенных типов данных. Однако в С++ программах обычно используется много тпов, определяемых пользователем, и нужно обрабатывать ввод и вывод также и значений этих типов. Очевидно, средство ввда/вывода должно быть простым, удобным, надежным в употреблнии, эффективным и гибким, и ко всему прочему полным. Ничье решение еще не смогло угодить всем, поэтому у пользователя должна быть возможность задавать альтернативные средства ввда/вывода и расширять стандартные средства ввода/вывода прменительно к требованиям приложения.
С++ разработан так, чтобы у пользователя была возмоность определять новые типы столь же эффективные и удобные, сколь и встроенные типы. Поэтому обоснованным является требвание того, что средства ввода/вывода для С++ должны обеспчиваться в С++ с применением только тех средств, которые дотупны каждому программисту. Описываемые здесь средства ввода/ вывода представляют собой попытку ответить на этот вызов.
Средства ввода/вывода «stream.h» связаны исключительно с обработкой преобразования типизированных объектов в последвательности символов и обратно. Есть и другие схемы ввода/ввода, но эта является основополагающей в системе UNIX, и большая часть видов двоичного ввода/вывода обрабатывается чрез рассмотрение символа просто как набора бит, при этом его общепринятая связь с алфавитом игнорируется. Тогда для прораммиста ключевая проблема заключается в задании соответствия между типизированным объектом и принципиально нетипизированой строкой.
Обработка и встроенных и определяемых пользователем тпов однородным образом и с гарантией типа достигается с пмощью одного перегруженного имени функции для набора функций вывода. Например:
put(cerr,"x = «); // cerr – поток вывода ошибок put(cerr,x); put(cerr,»\n");
Тип параметра определяет то, какая из функций put будет вызываться для каждого параметра. Это решение применялось в нескольких языках. Однако ему недостает лаконичности. Перерузка операции «„ значением «поместить в“ дает более хорошую
запись и позволяет программисту выводить ряд объектов одним оператором. Например:
cerr «„ "x = " «« x «« «\n“;
где cerr – стандартный поток вывода ошибок. Поэтому, ели x является int со значением 123, то этот оператор напечтает в стандартный поток вывода ошибок
x = 123
и символ новой строки. Аналогично, если X принадлежит определенному пользователем типу complex и имеет значение (1, 2.4), то приведенный выше оператор напечатает в cerr
x = (1,2.4)
Этот метод можно применять всегда, когда для x определна операция ««, и пользователь может определять операцию «« для нового типа.
8.2 Вывод
В этом разделе сначала обсуждаются средства форматного и бесформатного вывода встроенных типов, потом приводится стадартный способ спецификации действий вывода для определяемых пользователем типов.
8.2.1 Вывод Встроенных Типов
Класс ostream определяется вместе с операцией «„ («пместить в“) для обработки вывода встроенных типов:
class ostream (* // ... public: ostream amp; operator««(char*); ostream amp; operator««(int i) (* return *this««long(i); *) ostream amp; operator««(long); ostream amp; operator««(double);
ostream amp; put(char); *);
Функция operator«« возвращает ссылку на ostream, для кторого она была вызвана, чтобы к ней можно было применять другой ostream. Например:
cerr «« "x = " «« x;
где x является int, будет интерпретироваться как:
(cerr.operator««("x = ")).operator««(x);
В частности, отсюда следует, что когда один оператор ввода печатает несколько элементов, они будут печататься в ожидаемом порядке: слева направо. Наличие operator««, которая получает int, является избыточным, поскольку int может неявно преобразовываться в long. С другой стороны, int может преоразовываться также и в double. Наличие ostream::operator««(int) позволяет избежать этой неоднознаности. Для печати символов в виде символов предоставляется функция ostream::put(char), а ostream::operator««(int) печтает их целые значения.
8.2.2 Вывод Определяемых Пользователем Типов
Рассмотрим определяемый пользователем тип:
class complex (* double re, im; public: complex(double r = 0, double i = 0) (* re=r; im=i; *)
friend double real(complex amp; a) (* returna.re; *) friend double real(complex amp; a) (* returna.re; *)
friend complex operator+(complex, complex); friend complex operator-(complex, complex); friend complex operator*(complex, complex); friend complex operator/(complex, complex); // ... *);
Операцию «« для нового типа complex можно определить так:
ostream amp; operator««(ostream amp;s, complex z) (* return s «« "(" «« real(z) «« "," «« imag(z) «« ")"; *)
и использовать точно так же, как для встроенного типа:
complex x(1,2); // ... cout «„ "x = " «« x «« «\n“;
получая при этом
x = (1,2)