Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Бьерн Страуструп C++.doc
Скачиваний:
12
Добавлен:
07.11.2018
Размер:
2.45 Mб
Скачать

10.2 Вывод

Строгую типовую и единообразную работу как со встроенными, так и с пользовательскими типами можно обеспечить, если использовать единственное перегруженное имя функции для различных операций вывода. Например:

put(cerr,"x = "); // cerr - выходной поток ошибок

put(cerr,x);

put(cerr,'\n');

Тип аргумента определяет какую функцию надо вызывать в каждом случае. Такой подход применяется в нескольких языках, однако, это слишком длинная запись. За счет перегрузки операции << , чтобы она означала "вывести" ("put to"), можно получить более простую запись и разрешить программисту выводить в одном операторе последовательность объектов, например так:

cerr << "x = " << x << '\n';

Здесь cerr обозначает стандартный поток ошибок. Так, если х типа int со значением 123, то приведенный оператор выдаст

x = 123

и еще символ конца строки в стандартный поток ошибок. Аналогично, если х имеет пользовательский тип complex со значением (1,2.4), то указанный оператор выдаст

x = (1,2.4)

в поток cerr. Такой подход легко использовать пока x такого типа, для которого определена операция <<, а пользователь может просто доопределить << для новых типов.

Мы использовали операцию вывода, чтобы избежать многословности, неизбежной, если применять функцию вывода. Но почему именно символ << ? Невозможно изобрести новую лексему (см. 7.2). Кандидатом для ввода и вывода была операция присваивания, но большинство людей предпочитает, чтобы операции ввода и вывода были различны. Более того, порядок выполнения операции = неподходящий, так cout=a=b означает cout=(a=b). Пробовали использовать операции < и >, но к ним так крепко привязано понятие "меньше чем" и "больше чем", что операции ввода-вывода с ними во всех практически случаях не поддавались прочтению.

Операции << и >> похоже не создают таких проблем. Они асиметричны, что позволяет приписывать им смысл "в" и "из". Они не относятся к числу наиболее часто используемых операций над встроенными типами, а приоритет << достаточно низкий, чтобы писать арифметические выражения в качестве операнда без скобок:

cout << "a*b+c=" << a*b+c << '\n';

Скобки нужны, если выражение содержит операции с более низким приоритетом:

cout << "a^b|c=" << (a^b|c) << '\n';

Операцию сдвига влево можно использовать в операции вывода, но, конечно, она должна быть в скобках:

cout << "a<<b=" << (a<<b) << '\n';

10.2.1 Вывод встроенных типов

Для управления выводом встроенных типов определяется класс ostream с операцией << (вывести):

class ostream : public virtual ios {

// ...

public:

ostream& operator<<(const char*); //строки

ostream& operator<<(char);

ostream& operator<<(short i)

{ return *this << int(i); }

ostream& operator<<(int);

ostream& operator<<(long);

ostream& operator<<(double);

ostream& operator<<(const void*); // указатели

// ...

};

Естественно, в классе ostream должен быть набор функций operator<<() для работы с беззнаковыми типами.

Функция operator<< возвращает ссылку на класс ostream, из которого она вызывалась, чтобы к ней можно было применить еще раз operator<<. Так, если х типа int, то

cerr << "x = " << x;

понимается как

(cerr.operator<<("x = ")).operator<<(x);

В частности, это означает, что если несколько объектов выводятся с помощью одного оператора вывода, то они будут выдаваться в естественном порядке: слева - направо.

Функция ostream::operator<<(int) выводит целые значения, а функция ostream::operator<<(char) - символьные. Поэтому функция

void val(char c)

{

cout << "int('"<< c <<"') = " << int(c) << '\n';

}

печатает целые значения символов и с помощью программы

main()

{

val('A');

val('Z');

}

будет напечатано

int('A') = 65

int('Z') = 90

Здесь предполагается кодировка символов ASCII, на вашей машине может быть иной результат. Обратите внимание, что символьная константа имеет тип char, поэтому cout<<'Z' напечатает букву Z, а вовсе не целое 90.

Функция ostream::operator<<(const void*) напечатает значение указателя в такой записи, которая более подходит для используемой системы адресации. Программа

main()

{

int i = 0;

int* p = new int(1);

cout << "local " << &i

<< ", free store " << p << '\n';

}

выдаст на машине, используемой автором,

local 0x7fffead0, free store 0x500c

Для других систем адресации могут быть иные соглашения об изображении значений указателей.

Обсуждение базового класса ios отложим до 10.4.1.