На этом шаге мы рассмотрим способы определения состояния потока данных и связанные с ними логические условия.
В таблице 1 приведены две операторные функции, предназначенные для проверки состояния потоков данных в
логических выражениях.
Таблица 1. Потоковые операторы для логических выражений
Функция | Описание |
---|---|
operator void*() | роверяет нормальное состояние потока данных (эквивалент !fail()) |
operator! () | Проверяет ошибочное состояние потока данных (эквивалент fail()) |
Оператор void*() обеспечивает короткую и наглядную запись проверки состояния потока данных в
управляющих структурах:
// Пока стандартный поток ввода находится в нормальном состоянии... while (std::cin) { . . . . }
Проверка логических условий в управляющих структурах не требует прямого преобразования к bool. Достаточно
уникального преобразования к целому типу (например, int или char) или к типу указателя. Для чтения
объекта и проверки результата в одной команде часто требуется преобразование к void*:
if (std::cin >> x) { // Чтение х выполнено успешно . . . . }
Как уже было показано, следующее выражение возвращает cin:
std::cin >> х
Следовательно, после чтения х команда эквивалентна такой команде:
if (std::cin) { . . . . }
Объект cin часто используется при проверке условий; оператор void* этого объекта возвращает
признак наличия ошибки потока данных.
Этот прием обычно применяется в циклах, выполняющих чтение и обработку объектов:
// Пока удается прочитать obj while (std::cin >> obj) { // Обработка obj (в данном случае - простой вывод) std::cout << obj << std::endl; }
В этом фрагменте легко угадывается классический синтаксис фильтров С, примененный к объектам C++.
Цикл завершается при установке флага failbit или badbit. Эти флаги устанавливаются при
возникновении ошибки или достижении конца файла (попытка чтения за концом файла приводит к установке флага
eofbit или badbit). По умолчанию оператор >> игнорирует начальные пропуски. Обычно это
вполне устраивает программиста, но если obj относится к типу char, то пропуски могут оказаться
существенными. В этом случае в реализации фильтра можно использовать функции get() и put() потоковых
классов, а еще лучше - итератор istreambuf_iterator.
Оператор ! проверяет обратное условие. В соответствии со своим определением он возвращает true, если для
потока данных установлен бит failbit или badbit. Возможный вариант использования:
if (! std::cin) { // Поток cin находится в ошибочном состоянии . . . . }
Как и в случае с неявным преобразованием к логическому типу, этот оператор часто используется для совмещения
чтения с проверкой результата:
it (! (std::cin >> x)) { // Попытка чтения завершилась неудачей . . . . }
Следующее выражение возвращает объект cin, к которому применяется оператор !:
! std::cin >> x
Выражение после оператора ! необходимо заключить в круглые скобки из-за относительного приоритета операторов;
без круглых скобок оператор ! будет выполнен первым. Другими словами, следующие два выражения эквивалентны:
! std::cin >> х (!std::cin) >> х
Вряд ли это именно то, к чему стремился программист.
Хотя описываемые операторы очень удобны, следует обратить внимание на одну странность: двойное отрицание не
означает возвращения к исходному объекту:
- cin - потоковый объект класса istream;
- !!cin - логическая величина, описывающая состояние объекта cin.
Существует мнение, что преобразование к логическому значению не соответствует принципам хорошего стиля
программирования. Функции типа fail() обычно делают программу более понятной:
std::cin >> х; it (std::cin.fail()) { . . . . }
На следующем шаге мы рассмотрим состояние потока данных и исключения.