На этом шаге мы рассмотрим способы определения состояния потока данных и связанные с ними логические условия.
В таблице 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) >> х
Вряд ли это именно то, к чему стремился программист.
Хотя описываемые операторы очень удобны, следует обратить внимание на одну странность: двойное отрицание не означает возвращения к исходному объекту:
Существует мнение, что преобразование к логическому значению не соответствует принципам хорошего стиля программирования. Функции типа fail() обычно делают программу более понятной:
std::cin >> х; it (std::cin.fail()) { . . . . }
На следующем шаге мы рассмотрим состояние потока данных и исключения.