<< Пред.           стр. 98 (из 121)           След. >>

Список литературы по разделу

  cout << "Адрес pstr равен: "
  << pstr << endl;
 }
 Но после компиляции и запуска программа неожиданно выдает такую строку:
 
 Адрес pstr равен: vermeer
 
 Проблема в том, что тип const char* интерпретируется как C-строка. Чтобы все же напечатать адрес, хранящийся в pstr, необходимо подавить обработку типа const char* по умолчанию. Для этого мы сначала убираем спецификатор const, а затем приводим pstr к типу void*:
 << static_cast(const_cast(pstr))
 Теперь программа выводит ожидаемый результат:
 
 Адрес 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 ppals( pooh_pals, pooh_pals+4 );
 
  vector::iterator iter = ppals.begin();
  vector::iterator iter_end = ppals.end();
 
  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 ppals( pooh_pals, pooh_pals+4 );
 
  vector::iterator iter = ppals.begin();
  vector::iterator iter_end = ppals.end();
 
  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 ivec;
  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 : ; <<
  = > >::difference_type >::iterator ? allocator
 back_inserter(

<< Пред.           стр. 98 (из 121)           След. >>

Список литературы по разделу