Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Stoneshard |#11| Battle at the castle
Stoneshard |#10| A busy reaper
The Elder Scrolls IV: Oblivion Remastered - Trash review
Stoneshard |#9| A Million Liches

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Страустрап Б. Весь текст 579.17 Kb

Язык С++

Предыдущая страница Следующая страница
1 ... 31 32 33 34 35 36 37  38 39 40 41 42 43 44 ... 50
  }

  Для любой  переменной z типа, для которого определены операции <<
и >>, копирующий цикл можно написать так:

  while (cin>>z) cout << z << "\n";

Например,  если   z  -  вектор  символов,  этот  чикл  будет  брать
стандартный ввод и помещать его в стандартный вывод по одному слову
(то есть, последовательности символов без пробела) на строку.
  Когда в  качестве условия используется поток, происходит проверка
состояния потока    и  эта  проверка  проходит  успешно  (то  есть,
значение условия не ноль) только если состояние _good. В частности,
в  предыдущем   цикле  проверялось   состояние   istream,   которое
возвращает cin>>z.  Чтобы  обнаружить,  почему  цикл  или  проверка
закончились неудачно,  можно исследовать  состояние. Такая проверка
потока реализуется операцией преобразования (#6.3.2).
  Делать проверку  на  наличие  ошибок  каждого  ввода  или  вывода
действительно не  очень удобно,  и обычно  источником ошибок служит
программист, не  сделавший этого  в том месте, где это существенно.
Например, операции  вывода обычно  не  проверяются,  но  они  могут
случайно не сработать. Парадигма потока ввода/вывода построена так,
чтобы когда в C++ появится (если это произойдет) механизм обработки
исключительных ситуаций  (как средство  языка или  как  стандартная
библиотека)   его   будет   легко   применить   для   упрощения   и
стандартизации обработки ошибок в потоках ввода/вывода.

     8.4.3 Ввод Типов, Определяемых Пользователем

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

                             - стр 247 -

  istream& operator>>(istream& s, complex& a)
  /*
      форматы ввода для complex; "f" обозначает float:
      f
      ( f )
      ( f , f )
  */
  {
      double re = 0, im = 0;
      char c = 0;

      s >> c;
      if (c == '(') {
          s >> re >> c;
          if (c == ',') s >> im >> c;
          if (c != ')') s.clear(_bad);    // установить state
      }
      else {
          s.putback(c);
          s >> re;
      }

      if (s) a = complex(re,im);
      return s;
  }

  Несмотря на  то, что  не хватает  кода обработки  ошибок, большую
часть видов  ошибок это на самом деле обрабатывать будет. Локальная
переменная c  инициализируется,  чтобы  ее  значение  не  оказалось
случайно  '('   после  того,   как  операция   окнчится   неудачно.
Завершающая проверка  состояния потока  гарантирует,  что  значение
параметра a  будет изменяться  только в  том случае,  если все идет
хорошо.
  Операция установки  состояния названа  clear() (очистить), потому
что она  чаще всего  используется  для  установки  сосояния  потока
заново как _good. _good является значением параметра по умолчанию и
для istream::clear(), и для ostream::clear().
  Над операциями  ввода надо  поработать еще. Было бы, в частности,
замечательно, если  бы можно  было задавать ввод в терминах шаблона
(как в  языках Снобол и Икон), а потом проверять, прошла ли успешна
вся  операция  ввода.  Такие  операции  должны  были  бы,  конечно,
обеспечивать некоторую  дополнительную буферизацию, чтобы они могли
воссанавливать поток ввода в его исходное состояние после неудачной
попытки распознавания.

     8.4.4 Инициализация Потоков Ввода

  Естественно,  тип   istream,  так   же  как  и  ostream,  снабжен
конструктором:

                             - стр 248 -

  class istream {
      // ...
      istream(streambuf* s, int sk =1, ostream* t =0);
      istream(int size, char* p, int sk =1);
      istream(int fd, int sk =1, ostream* t =0);
  };

Параметр sk  задает, должны пропускаться пропуски или нет. Параметр
t  (необязательный)   задает  указатель   на  ostream,  к  которому
прикреплен istream.  Например, cin  прикреплен к  cout; это значит,
что перед  тем, как  попытаться читать символы из своего файла, cin
выполняет

  cout.flush(); // пишет буфер вывода

  С помощью функции istream::tie() можно прикрепить (или открепить,
с помощью tie(0)) любой ostream к любому istream. Например:

  int y_or_n(ostream& to, istream& from)
  /*
       "to", получает отклик из "from"
  */
  {
      ostream* old = from.tie(&to);
      for (;;) {
          cout << "наберите Y или N: ";
          char ch = 0;
          if (!cin.get(ch)) return 0;

          if (ch != '\n') { // пропускает остаток строки
              char ch2 = 0;
              while (cin.get(ch2) && ch2 != '\n') ;
          }
          switch (ch) {
          case 'Y':
          case 'y':
          case '\n':
              from.tie(old);        // восстанавливает старый tie
              return 1;
          case 'N':
          case 'n':
              from.tie(old);        // восстанавливает старый tie
              return 0;
          default:
              cout << "извините, попробуйте еще раз: ";
          }
      }
  }

Когда используется  буферизованный  ввод  (как  это  происходит  по
умолчанию), пользователь  не может набрав только одну букву ожидать
отклика. Система  ждет появвения  символа  новой  строки.  y_or_n()
смотрит на первыйй символ строки, а остальные игноиррует.
  Символ   можно    вернуть   в    поток    с    помощью    функции
istream::putback(char).  Это   позволяет   программе   "заглядывать
вперед" в поток ввода.

                             - стр 249 -

     8.5 Работа со Строками

  Можно   осуществлять   действия,   подобные   вводу/выводу,   над
символьным  вектором,   прикрепляя  к  нему  istream  или  ostream.
Например, если веатор содержит обычную строку, завершающуюся нулем,
для печати  слов из  этого вектора  можно использовать  приведенный
выше копирующий цикл:

  void word_per_line(char v[], int sz)
  /*
      печатет "v" размера "sz" по одному слову на строке
  */
  {
      istream ist(sz,v); // сделать istream для v
      char b2[MAX];      // больше наибольшего слова
      while (ist>>b2) cout << b2 << "\n";
  }

Завершающий нулевой  символ  в  этом  случае  интерпретируется  как
символ конца файла.
  В помощью  ostream можно  отформатировать сообщения,  которые  не
нужно печатать тотчас же:

  char* p = new char[message_size];
  ostream ost(message_size,p);
  do_something(arguments,ost);
  display(p);

  Такая операция,  как do_something,  может  писать  в  поток  ost,
передавать ost  своим  подоперациям  и  т.д.  спомощью  стандартных
операций вывода. Нет необходимости делать проверку не переполнение,
поскольку ost  знает свою  длину и когда он будет переполняться, он
будет переходить  в состояние  _fail.  И,  наконец,  display  может
писать сообщения  в "настоящий"  поток  вывода.  Этот  метод  может
оказаться наиболее  полезным, чтобы  справляться  с  ситуациями,  в
которых окончательное  отображение данных  включает  в  себя  нечто
более сложное,  чем работу  с традиционным  построчным  устройством
вывода. Например,  текст из ost мог бы помещаться в располагающуюся
где-то на экране область фиксированного размера.

     8.6 Буферизация

  При задании  операций ввода/вывода  мы никак  не  касались  типов
файлов, но  ведь не  все устройства можно рассматривать одинаково с
точки  зрения   стратегии  буферизации.   Например,  для   ostream,
подключенного к  символьной строке,  требуется буферизация  другого
вида, нежели  для ostream, подключенного к файлу. С этими пробемами
можно  справиться,  задавая  различные  буферные  типы  для  разных
потоков  в   момент  инициализации   (обратите  внимание   на   три
конструктора класса  ostream). Есть  только один набор операций над
этими буферными  типами, поэтому  в функциях  ostream нет  кода, их
различающего. Однако  функции,  которые  обрабатывают  переполнение
сверху и  снизу, виртуальные. Этого достаточно, чтобы справляться с
необходимой в данное время стратегией буферизации. Это также служит
хорошим примером  применения виртуальных  функций для  того,  чтобы

                             - стр 250 -

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

  struct streambuf {      // управление буфером потока

      char* base;         // начало буфера
      char* pptr;         // следующий свободный char
      char* qptr;         // следующий заполненный char
      char* eptr;         // один из концов буфера
      char  alloc;        // буфер, выделенный с помощью new

          // Опустошает буфер:
          // Возвращает EOF при ошибке и 0 в случае успеха
      virtual int overflow(int c =EOF);

          // Заполняет буфер
          // Возвращет EOF при ошибке или конце ввода,
          // иначе следующий char
      virtual int underflow();

      int snextc()        // берет следующий char
      {
          return (++qptr==pptr) ? underflow() : *qptr&0377;
      }

      // ...

      int allocate()      // выделяет некоторое пространство буфера

      streambuf() { /* ... */}
      streambuf(char* p, int l) { /* ... */}
      ~streambuf() { /* ... */}
  };

  Обратите внимание,  что здесь определяются указатели, необходимые
для работы  с буфером,  поэтому обычные посимвольные действия можно
определить (только один раз) в виде максимально эффективных inline-
функций. Для  каждой конкретной  стратегии  буферизации  необходимо
определять только  функции переполнения  overflow() и  underflow().
Например:

                             - стр 251 -

  struct filebuf : public streambuf {

      int fd;              // дескриптор файла
      char opened;         // файл открыт

      int overflow(int c =EOF);
      int underflow();

      // ...

          // Открывает файл:
          // если не срабатывает, то возвращет 0,
          // в случае успеха возвращает "this"
      filebuf* open(char *name, open_mode om);
      int close() { /* ... */ }

      filebuf() { opened = 0; }
      filebuf(int nfd) { /* ... */ }
      filebuf(int nfd, char* p, int l) : (p,l) { /* ... */ }
      ~filebuf() { close(); }
  };

  int filebuf::underflow()         // заполняет буфер из fd
  {
      if (!opened || allocate()==EOF) return EOF;

      int count = read(fd, base, eptr-base);
      if (count < 1) return EOF;

      qptr = base;
      pptr = base + count;
      return *qptr & 0377;
  }

     8.7 Эффективность

  Можно было  бы ожидать, что раз ввод/вывод  определен с
помощью обцедоступных средств языка, он будет менее эффективен, чем
встроенное средство.  На самом  деле это не так. Для действий вроде
"поместить   символ    в   поток"    используются   inline-функции,
единственные необходимые  на этом  уровне вызовы  функций возникают
из-за переполнения  сверху и  снизу. Для  простых объектов  (целое,
строка  и   т.п.)  требуется   по  одному  вызову  на  каждый.  Как
выясняется, это  не  отличается  от  прочих  средств  ввода/вывода,
работающих с объектами на этом уровне.

     8.8 Упражнения

  1. (*1.5) Считайте  файл чисел  с плавающей  точкой, составьте из
     пар считанных  чисел комплексные  числа и выведите комплексные
     числа.

                             - стр 252 -

  2. (*1.5)   Определите    тип   name_and_address   (имя_и_адрес).
     Определите  для  него  <<  и  >>.  Скопируйте  поток  объектов
     name_and_address.
  3. (*2)  Постройте   несколько  функций   для  запроса  и  чтения
     различного  вида   информации.  Простейший  пример  -  функция
     y_or_n() в  #8.4.4. Идеи: целое, число с плавающей точкой, имя
     файла, почтовый адрес, дата, личные данные и т.д. Постарайтесь
     сделать их защищенными от дурака.
  4. (*1.5) Напишите  программу, которая  печатает (1)  все буквы в
     нижнем регистре, (2) все буквы, (3) все буквы и цифры, (4) все
     символы, которые  могут встречаться  в идентификаторах  C++ на
Предыдущая страница Следующая страница
1 ... 31 32 33 34 35 36 37  38 39 40 41 42 43 44 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама