<< Пред. стр. 98 (из 121) След. >>
cout << "Адрес pstr равен: "<< pstr << endl;
}
Но после компиляции и запуска программа неожиданно выдает такую строку:
Адрес pstr равен: vermeer
Проблема в том, что тип const char* интерпретируется как C-строка. Чтобы все же напечатать адрес, хранящийся в pstr, необходимо подавить обработку типа const char* по умолчанию. Для этого мы сначала убираем спецификатор const, а затем приводим pstr к типу void*:
<< static_cast
Теперь программа выводит ожидаемый результат:
Адрес pstr равен: 0x116e8
А вот еще одна загадка. Нужно напечатать большее из двух чисел:
#include
inline void
max_out( int val1, int val2 )
{
cout << ( val1 > val2 ) ? val1 : val2;
}
int main()
{
int ix = 10, jx = 20;
cout << "Большее из " << ix
<< ", " << jx << " равно ";
max_out( ix, jx );
cout << endl;
}
Однако программа выдает неправильный результат:
Большее из 10, 20 равно 0
Проблема в том, что оператор вывода имеет более высокий приоритет, чем оператор условного выражения, поэтому печатается результат сравнения val1 и val2. Иными словами, выражение
cout << ( val1 > val2 ) ? val1 : val2;
вычисляется как
(cout << ( val1 > val2 )) ? val1 : val2;
Поскольку val1 не больше val2, то результатом сравнения будет false, обозначаемый нулем. Чтобы изменить приоритет операций, весь оператор условного выражения следует заключить в скобки:
cout << ( val1 > val2 ? val1 : val2 );
Теперь результат получается правильный:
Большее из 10, 20 равно 20
Такого рода ошибку было бы проще найти, если бы значения литералов true и false типа bool печатались как строки, а не как 1 и 0. Тогда мы увидели бы строку:
Большее из 10, 20 равно false
и все стало бы ясно. По умолчанию литерал false печатается как 0, а true – как 1. Это можно изменить, воспользовавшись манипулятором boolalpha(), что и сделано в следующей программе:
int main()
{
cout << "печать значений типа bool по умолчанию: "
<< true << " " << false
<< "\nи в виде строк: "
<< boolalpha()
<< true << " " << false
<< endl;
}
Вот результат:
печать значений типа bool по умолчанию: 1 0
и в виде строк: true false
Для вывода массива, а также вектора или отображения, необходимо обойти все элементы и напечатать каждый из них:
#include
#include
#include
string pooh_pals[] = {
"Тигра", "Пятачок", "Иа-Иа", "Кролик"
};
int main()
{
vector
vector
vector
cout << "Это друзья Пуха: ";
for ( ; iter != iter_end; iter++ )
cout << *iter << " ";
cout << endl;
}
Вместо того чтобы явно обходить все элементы контейнера, выводя каждый по очереди, можно воспользоваться потоковым итератором ostream_iterator. Так выглядит эквивалентная программа, где используется эта техника (подробное обсуждение итератора ostream_iterator см. в разделе 12.4):
#include
#include
#include
#include
string pooh_pals[] = {
"Тигра", "Пятачок", "Иа-Иа", "Кролик"
};
int main()
{
vector
vector
vector
cout << "Это друзья Пуха: ";
// копируем каждый элемент в cout ...
ostream_iterator< string > output( cout, " " );
copy( iter, iter_end, output );
cout << endl;
}
Программа печатает такую строку:
Это друзья Пуха: Тигра Пятачок Иа-Иа Кролик
Упражнение 20.1
Даны следующие определения объектов:
string sa[4] = { "пух", "тигра", "пятачок", "иа-иа" };
vector< string > svec( sa, sa+4 );
string robin( "кристофер робин" );
const char *pc = robin.c_str();
int ival = 1024;
char blank = ' ';
double dval = 3.14159;
complex purei( 0, 7 );
Направьте значение каждого объекта в стандартный вывод.
Напечатайте значение адреса pc.
Напечатайте наименьшее из двух значений ival и dval, пользуясь оператором условного выражения:
ival < dval ? ival : dval
20.2. Ввод
Основное средство реализации ввода – это оператор сдвига вправо (>>). Например, в следующей программе из стандартного ввода читается последовательность значений типа int и помещается в вектор:
#include
#include
int main()
{
vector
int ival;
while ( cin >> ival )
ivec.push_back( ival );
// ...
}
Подвыражение
cin >> ival;
читает целое число из стандартного ввода и копирует его в переменную ival. Результатом является левый операнд – объект класса istream, в данном случае cin. (Как мы увидим, это позволяет сцеплять операторы ввода.)
Выражение
while ( cin >> ival )
читает последовательность значений, пока cin не станет равно false. Значение istream может быть равно false в двух случаях: достигнут конец файла (т.е. все значения из файла прочитаны успешно) или встретилось неверное значение, скажем 3.14159 (десятичная точка недопустима в целом числе), 1e-1 (буква e недопустима) или любой строковый литерал. Если вводится неверное значение, объект istream переводится в состояние ошибки и чтение прекращается. (В разделе 20.7 мы подробнее расскажем о таких состояниях.)
Есть набор предопределенных операторов ввода, принимающих аргументы любого встроенного типа, включая C-строки, а также стандартных библиотечных типов string и complex:
#include
#include
int main()
{
int item_number;
string item_name;
double item_price;
cout << "Пожалуйста, введите item_number, item_name и price: "
<< endl;
cin >> item_number;
cin >> item_name;
cin >> item_price;
cout << "Введены значения: item# "
<< item_number << " "
<< item_name << " @$"
<< item_price << endl;
}
Вот пример выполнения этой программы:
Пожалуйста, введите item_number, item_name и price:
10247 widget 19.99
Введены значения: item# 10247 widget @$19.99
Можно ввести каждый элемент на отдельной строке. По умолчанию оператор ввода отбрасывает все разделяющие пустые символы: пробел, символ табуляции, символ перехода на новую строку, символ перевода страницы и символ возврата каретки. (О том, как отменить это поведение, см. в разделе 20.9.)
Пожалуйста, введите item_number, item_name и price:
10247
widget
19.99
Введены значения: item# 10247 widget @$19.99
При чтении ошибка iostream более вероятна, чем при записи. Если мы вводим такую последовательность:
// ошибка: item_name должно быть вторым
BuzzLightyear 10009 8.99
то инструкция
cin >> item_number;
закончится ошибкой ввода, поскольку BuzzLightyear не принадлежит типу int. При проверке объекта istream будет возвращено false, поскольку возникло состояние ошибки. Более устойчивая к ошибкам реализация выглядит так:
cin >> item_number;
if ( ! cin )
cerr << "ошибка: введено некорректное значение item_number!\n";
Хотя сцепление операторов ввода поддерживается, проверить корректность каждой отдельной операции нельзя, поэтому пользоваться таким приемом следует лишь тогда, когда ошибка невозможна. Наша программа теперь выглядит так:
#include
#include
int main()
{
int item_number;
string item_name;
double item_price;
cout << "Пожалуйста, введите item_number, item_name и price: "
<< endl;
// хорошо, но легче допустить ошибку
cin >> item_number >> item_name >> item_price;
cout << "Введены значения: item# "
<< item_number << " "
<< item_name << " @$"
<< item_price << endl;
}
Последовательность
ab c
d e
составлена из девяти символов: 'a', 'b', ' ' (пробел), 'c', '\n' (переход на новую строку), 'd', '\t' (табуляция), 'e' и '\n'. Однако приведенная программа читает лишь пять букв:
#include
int main()
{
char ch;
// прочитать и вывести каждый символ
while ( cin >> ch )
cout << ch;
cout << endl;
// ...
}
И печатает следующее:
abcde
По умолчанию все пустые символы отбрасываются. Если нам нужны и они, например для сохранения формата входного текста или обработки пустых символов (скажем, для подсчета количества символов перехода на новую строку), то можно воспользоваться функцией-членом get() класса istream (обычно в паре с ней употребляется функция-член put() класса ostream; они будут рассмотрены ниже). Например:
#include
int main()
{
char ch;
// читать все символы, в том числе пробельные
while ( cin.get( ch ))
cout.put( ch );
// ...
}
Другая возможность сделать это – использовать манипулятор noskipws.
Каждая из двух данных последовательностей считается составленной из пяти строк, разделенных пробелами, если для чтения используются операторы ввода с типами const char* или string:
A fine and private place
"A fine and private place"
Наличие кавычек не делает пробелы внутри закавыченной строки ее частью. Просто открывающая кавычка становится начальным символом первого слова, а закрывающая – конечным символом последнего.
Вместо того чтобы читать из стандартного ввода по одному символу, можно воспользоваться потоковым итератором istream_iterator:
#include
#include
#include
#include
int main()
{
istream_iterator< string > in( cin ), eos ;
vector< string > text ;
// копировать прочитанные из стандартного ввода значения
// в вектор text
copy( in , eos , back_inserter( text ) ) ;
sort( text.begin() , text.end() ) ;
// удалить дубликаты
vector< string >::iterator it;
it = unique( text.begin() , text.end() ) ;
text.erase( it , text.end() ) ;
// вывести получившийся вектор
int line_cnt = 1 ;
for ( vector< string >::iterator iter = text.begin() ;
iter != text.end() ; ++iter , ++line_cnt )
cout << *iter
<< ( line_cnt % 9 ? " " : "\n" ) ;
cout << endl;
}
Пусть входом для этой программы будет файл istream_iter.C с исходным текстом. В системе UNIX мы можем перенаправить стандартный ввод на файл следующим образом (istream_iter – имя исполняемого файла программы):
istream_iter < istream_iter.C
(Для других систем необходимо изучить документацию.) В результате программа выводит:
!= " " "\n" #include % ( ) *iter ++iter
++line_cnt , 1 9 : ; <<
back_inserter(