На этом шаге мы рассмотрим особенности использования исключений при работе с потоками.
Механизм обработки исключений был включен в C++ для выявления ошибок и особых ситуаций. Тем не менее это было сделано уже после того, как потоки данных получили широкое распространение. Чтобы сохранить совместимость, потоки данных по умолчанию не генерируют исключений. Однако для каждого флага состояния стандартизированных потоков данных можно указать, должна ли установка этого флага сопровождаться выдачей исключения. Для этой цели используется функция exceptions() (таблица 1).
Функция | Описание |
---|---|
exceptions (флаги) | Определяет флаги, при установке которых должно генерироваться исключение |
exceptions() | Возвращает флаги, при установке которых должно генерироваться исключение |
При вызове без аргументов функция exceptions() возвращает текущие флаги, при установке которых генерируются исключения. Если функция возвращает goodbit, исключения не генерируются. Этот режим используется по умолчанию для поддержания совместимости. Если функция exceptions() вызывается с аргументом, то сразу же после установки соответствующего флага состояния будет выдано исключение.
Следующий пример настраивает поток данных так, чтобы при установке любого флага генерировалось исключение:
// Генерировать исключение при любой "ошибке"
strm.exceptions (std::ios::eofbit | std::ios::failbit | std::ios::badbit);
Если аргумент равен 0 или goodbit, исключения не генерируются:
// Не генерировать исключения
strm.exceptions (std::ios::goodbit);
Исключения генерируются при установке соответствующих флагов после вызова clear() или setstate(). Это происходит даже в том случае, если флаг был установлен ранее:
// Вызов генерирует исключение, если флаг failbit был установлен при входе strm.exceptions (std::ios::failbit); . . . . . // Следующая команда генерирует исключение (даже если флаг failbit // был установлен ранее) strm.setstate (std::ios::failbit);
Генерируемые исключения являются объектами класса std::ios_base::failure, производного от класса exception.
namespace std { class ios_base::failure : public exception { public: // Конструктор explicit failure (const string& msg); // Деструктор virtual ~failure(); // Возврат информации об исключении virtual const char* what() const; }; }
К сожалению, стандарт не требует, чтобы объект исключения содержал какую-либо информацию об ошибочном потоке данных или о типе ошибки. Существует единственный переносимый способ получения информации об ошибке - сообщение, возвращаемое функцией what(). Впрочем, переносим только вызов what(), но не возвращаемая строка. Если требуется дополнительная информация, программист должен сам заботиться о ее получении.
Из этого поведения следует, что обработка исключений в большей степени ориентируется на непредвиденные ситуации. Собственно, поэтому она называется обработкой исключений, а не обработкой ошибок. Ожидаемые ошибки (например, ошибки форматирования при вводе данных пользователем) считаются "нормальными", а для их обработки лучше использовать флаги состояния.
Основная область применения потоковых исключений - чтение предварительно отформатированных данных (например, автоматически сгенерированных файлов). Но даже в этом случае при обработке исключений возникают проблемы. Например, если данные читаются до конца файла, вы не сможете генерировать исключения ошибок без того, чтобы не получить исключение конца файла. Дело в том, что при обнаружении конца файла также устанавливается бит failbit (признак неудачи при чтении объекта). Чтобы отличить конец файла от ошибки ввода, придется дополнительно проверить состояние потока данных.
На следующем шаге мы рассмотрим пример использования исключений при работе с потоками.