Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

1070

Дополнительные аргументы printf() на позиционной основе сопоставляются со спецификаторами формата, начинающимися со знака %. Все остальные символы в форматной строке рассматриваются как литералы и выводятся буквально.

Основные недостатки семейства функций printf() таковы: во-первых, форматная строка не обобщается на определенные пользователем типы, и, во-вторых, если типы или число аргументов не соответствуют форматной строке, компилятор не заметит ошибки, а вывод будет отформатирован неверно. Однако у функций printf() есть и достоинство компактность записи.

1.Получите так же отформатированный результат с помощью объекта класса ostringstream.

2.Сформулируйте достоинства и недостатки обоих подходов.

20.9. Состояние формата

Каждый объект класса из библиотеки iostream поддерживает состояние формата, которое управляет выполнением операций форматирования, например основание системы счисления для целых значений или точность для значений с плавающей точкой.

Для модификации состояния формата объекта в распоряжении программиста имеется предопределенный набор манипуляторов.1 Манипулятор применяется к потоковому объекту так же, как к данным. Однако вместо чтения или записи данных манипулятор модифицирует внутреннее состояние потока. Например, по умолчанию объект типа bool,

#include <iostream.h>

int main()

{

bool illustrate = true;

cout << "объект illustrate типа bool установлен в true: " << illustrate << '\n';

имеющий значение true (а также литеральная константа true), выводится как целая ‘1’:

}

Чтобы поток cout выводил переменную illustrate в виде слова true, мы применяем манипулятор boolalpha:

Примечание [O.A.6]: Нумера ция сносок сбита.

1 Кроме того, программист может устанавливать и сбрасывать флаги состояния формата с помощью функций-членов setf() и unsetf(). Мы их рассматривать не будем; исчерпывающие ответы на вопросы, относящиеся к этой теме, можно получить в [STROUSTRUP97].

С++ для начинающих

1071

#include <iostream.h>

int main()

{

bool illustrate = true;

cout << "объект illustrate типа bool установлен в true: ";

//изменяет состояние cout так, что булевские значения

//печатаются в виде строк true и false

cout << boolalpha;

cout << illustrate << '\n';

}

Поскольку манипулятор возвращает потоковый объект, к которому он применялся, то допустимо прицеплять его к выводимым данным и другим манипуляторам. Вот как

#include <iostream.h>

int main()

{

bool illustrate = true;

cout << "объект illustrate типа bool: "

<<illustrate

<<"\nс использованием boolalpha: "

<<boolalpha << illustrate << '\n';

//...

можно перемежать данные и манипуляторы в нашей программе:

}

Вывод данных и манипуляторов вперемежку может сбить пользователя с толку.

Применение манипулятора изменяет не только представление следующего за ним объекта, но и внутреннее состояние потока. В нашем примере все значения типа bool в оставшейся части программы также будут выводиться в виде строк.

Чтобы отменить сделанную модификацию потока cout, необходимо использовать

cout << boolalpha // устанавливает внутреннее состояние cout << illustrate

манипулятор noboolalpha:

<< noboolalpha // сбрасывает внутреннее состояние cout

Как мы покажем, для многих манипуляторов имеются парные.

По умолчанию значения арифметических типов читаются и записываются в десятичной системе счисления. Программист может изменить ее на восьмеричную или шестнадцатеричную, а затем вернуться к десятичной (это распространяется только на целые типы, но не на типы с плавающей точкой), пользуясь манипуляторами hex, oct и dec:

С++ для начинающих

1072

#include <iostream> int main()

{

int ival = 16; double dval = 16.0;

cout << "ival: " << ival

<< " установлен oct: " << oct << ival << "\n";

cout << "dval: " << dval

<< " установлен hex: " << hex << dval << "\n";

cout << "ival: " << ival

<< " установлен dec: " << dec << ival << "\n";

}

Эта программа печатает следующее:

ival: 16 установлен oct: 20 dval: 16 установлен hex: 16 ival: 10 установлен dec: 16

Но, глядя на значение, мы не можем понять, в какой системе счисления оно записано. Например, 20 – это действительно 20 или восьмеричное представление 16? Манипулятор showbase выводит основание системы счисления вместе со значением с помощью следующих соглашений:

0x в начале обозначает шестнадцатеричную систему (если мы хотим, чтобы вместо строчной буквы 'x' печаталась заглавная, то можем применить манипулятор uppercase, а для отмены манипулятор nouppercase);

0 в начале обозначает восьмеричную систему;

отсутствие того и другого обозначает десятичную систему.

#include <iostream> int main()

{

int ival = 16; double dval = 16.0;

cout << showbase;

cout << "ival: " << ival

<< " установлен oct: " << oct << ival << "\n";

cout << "dval: " << dval

<< " установлен hex: " << hex << dval << "\n";

cout << "ival: " << ival

<< " установлен dec: " << dec << ival << "\n"; cout << noshowbase;

Вот та же программа, но и с использованием showbase:

}

С++ для начинающих

1073

Результат:

ival: 16 установлен oct: 020 dval: 16 установлен hex: 16 ival: 0x10 установлен dec: 16

Манипулятор noshowbase восстанавливает состояние cout, при котором основание системы счисления не выводится.

По умолчанию значения с плавающей точкой выводятся с точностью 6. Эту величину можно модифицировать с помощью функции-члена precision(int) или манипулятора setprecision(); для использования последнего необходимо включить заголовочный

#include <iostream> #include <iomanip> #include <math.h>

int main()

{

cout << "Точность: "

<<cout.precision() << endl

<<sqrt(2.0) << endl;

cout.precision(12);

cout << "\nТочность: "

<<cout.precision() << endl

<<sqrt(2.0) << endl;

cout << "\nТочность: " << setprecision(3)

<<cout.precision() << endl

<<sqrt(2.0) << endl;

return 0;

файл iomanip. precision() возвращает текущее значение точности. Например:

}

После компиляции и запуска программа печатает следующее:

Точность: 6 1.41421

Точность: 12 1.41421356237

Точность: 3 1.41

Манипуляторы, принимающие аргумент, такие, как setprecision() и setw(), требуют включения заголовочного файла iomanip:

#include <iomanip>

Кроме описанных аспектов, setprecision() имеет еще два: на целые значения он не оказывает никакого влияния; значения с плавающей точкой округляются, а не

С++ для начинающих

1074

обрезаются. Таким образом, при точности 4 значение 3.14159 печатается как 3.142, а при точности 3 – как 3.14.

По умолчанию десятичная точка не печатается, если дробная часть значения равна 0. Например:

cout << 10.00

выводит

10

cout << showpoint << 10.0

Чтобы точка выводилась, воспользуйтесь манипулятором showpoint:

<< noshowpoint << '\n';

Манипулятор noshowpoint восстанавливает поведение по умолчанию.

По умолчанию значения с плавающей точкой выводятся в нотации с фиксированной точкой. Для перехода на научную нотацию используется идентификатор scientific, а

cout << "научная: " << scientific

<<10.0

<<"с фиксированной точкой: " << fixed

для возврата к прежней нотации модификатор fixed:

<<10.0 << '\n';

Врезультате печатается:

научная: 1.0e+01

с фиксированной точкой: 10

Если бы мы захотели вместо буквы 'e' выводить 'E', то следовало бы употребить манипулятор uppercase, а для возврата к 'e' nouppercase. (Манипулятор uppercase не приводит к переводу букв в верхний регистр при печати.)

По умолчанию перегруженные операторы ввода пропускают пустые символы (пробелы, знаки табуляции, новой строки и возврата каретки). Если дана последовательность:

a bc d

то цикл

С++ для начинающих

1075

char ch;

while ( cin >> ch )

// ...

читает все буквы от 'a' до 'd' за четыре итерации, а пробельные разделители оператором ввода игнорируются. Манипулятор noskipws отменяет такой пропуск пробельных

char ch;

cin >> noskipws; while ( cin >> ch )

// ...

символов:

cin >> skipws;

Теперь цикл while будет выполняться семь раз. Чтобы восстановить поведение по умолчанию, к потоку cin применяется манипулятор skipws.

Когда мы пишем:

cout << "пожалуйста, введите значение: ";

то в буфере потока cout сохраняется литеральная строка. Есть ряд условий, при которых буфер сбрасывается (т.е. опустошается), – в нашем случае в стандартный вывод:

буфер может заполниться. Тогда перед чтением следующего значения его необходимо сбросить;

буфер можно сбросить явно с помощью любого из манипуляторов flush, ends

//сбрасывает буфер cout << "hi!" << flush;

//вставляет нулевой символ, затем сбрасывает буфер char ch[2]; ch[0] = 'a'; ch[1] = 'b';

cout << ch << ends;

// вставляет символ новой строки, затем сбрасывает буфер

или endl:

cout << "hi!" << endl;

при установлении внутренней переменной состояния потока unitbuf буфер сбрасывается после каждой операции вывода;

объект ostream может быть связан (tied) с объектом istream. Тогда буфер ostream сбрасывается каждый раз, когда istream читает из входного потока. cout всегда связан с cin:

cin.tie( &cout );

Инструкция

cin >> ival;

С++ для начинающих

1076

приводит к сбросу буфера cout.

В любой момент времени объект ostream разрешено связывать только с одним объектом istream. Чтобы разорвать существующую связь, мы передаем функции-члену tie()

istream is; ostream new_os;

//...

//tie() возвращает существующую связь ostream *old_tie = is.tie();

is.tie( 0 ); // разорвать существующую связь is.tie( &new_os ); // установить новую связь

// ...

is.tie( 0 ); // разорвать существующую связь

значение 0:

is.tie( old_tie ); // восстановить прежнюю связь

Мы можем управлять шириной поля, отведенного для печати числового или строкового

#include <iostream> #include <iomanip>

int main()

{

int ival = 16;

double dval = 3.14159;

cout << "ival: " << setw(12) << ival << '\n' << "dval: " << setw(12) << dval << '\n';

значения, с помощью манипулятора setw(). Например, программа

}

печатает:

ival: 16 dval: 3.14159

Второй модификатор setw() необходим потому, что, в отличие от других манипуляторов, setw() не изменяет состояние формата объекта ostream.

Чтобы выровнять значение по левой границе, мы применяем манипулятор left (соответственно манипулятор right восстанавливает выравнивание по правой границе). Если мы хотим получить такой результат:

16

-3

С++ для начинающих

1077

то пользуемся манипулятором internal, который выравнивает знак по левой границе, а значение по правой, заполняя пустое пространство пробелами. Если же нужен другой символ, то можно применить манипулятор setfill(). Так

cout << setw(6) << setfill('%') << 100 << endl;

печатает:

%%%100

В табл. 20.1 приведен полный перечень предопределенных манипуляторов.

Таблица 20.1. Манипуляторы

Манипулятор

Назначение

 

 

boolalpha

Представлять true и false в виде строк

*noboolalpha

Представлять true и false как 1 и 0

Showbase

Печатать префикс, обозначающий систему счисления

*noshowbase

Не печатать префикс системы счисления

showpoint

Всегда печатать десятичную точку

*noshowpoint

Печатать десятичную точку только в том случае, если дробная часть

 

ненулевая

showpos

Печатать + для неотрицательных чисел

*noshowpos

Не печатать + для неотрицательных чисел

Манипулятор

Назначение

*skipws

Пропускать пробельные символы в операторах ввода

noskipws

Не пропускать пробельные символы в операторах ввода

uppercase

Печатать 0X при выводе в шестнадцатеричной системе счисления;

*nouppercase

E при выводе в научной нотации

Печатать 0x при выводе в шестнадцатеричной системе счисления;

 

e при выводе в научной нотации

*dec

Печатать в десятичной системе

hex

Печатать в шестнадцатеричной системе

oct

Печатать в восьмеричной системе