Состояния потока
С каждым потоком (istream или ostream) связано определенное состояние. Нестандартные ситуации и ошибки обрабатываются с помощью проверки и установки состояния подходящим образом.
Узнать состояние потока можно с помощью операций над классом ios:
class ios { //ios является базовым для ostream и istream //... public: int eof() const; // дошли до конца файла int fail() const; // следующая операция будет неудачна int bad() const; // поток испорчен int good() const; // следующая операция будет успешной //... };
Последняя операция ввода считается успешной, если состояние задается good() или eof(). Если состояние задается good(), то последующая операция ввода может быть успешной, в противном случае она будет неудачной. Применение операции ввода к потоку в состоянии, задаваемом не good(), считается пустой операцией. Если произошла неудача при попытке чтения в переменную v, то значение v не изменилось (оно не изменится, если v имеет тип, управляемый функциями члена из istream или ostream). Различие между состояниями, задаваемыми как fail() или как bad() уловить трудно, и оно имеет смысл только для разработчиков операций ввода. Если состояние есть fail(), то считается, что поток не поврежден, и никакие символы не пропали; о состоянии bad() ничего сказать нельзя.
Значения, обозначающие эти состояния, определены в классе ios:
class ios { //... public: enum io_state { goodbit=0, eofbit=1, filebit=2, badbit=4, }; //... };
Истинные значения состояний зависят от реализации, и указанные значения приведены только, чтобы избежать синтаксически неправильных конструкций.
Проверять состояние потока можно следующим образом:
switch (cin.rdstate()) { case ios::goodbit: // последняя операция с cin была успешной break; case ios::eofbit: // в конце файла break; case ios::filebit: // некоторый анализ ошибки // возможно неплохой break; case ios::badbit: // cin возможно испорчен break; }
В более ранних реализациях для значений состояний использовались глобальные имена. Это приводило к нежелательному засорению пространства именования, поэтому новые имена доступны только в пределах класса ios. Если вам необходимо использовать старые имена в сочетании с новой библиотекой, можно воспользоваться следующими определениями:
const int _good = ios::goodbit; const int _bad = ios::badbit; const int _file = ios::filebit; const int _eof = ios::eofbit;
typedef ios::io_state state_value ;
Разработчики библиотек должны заботится о том, чтобы не добавлять новых имен к глобальному пространству именования. Если элементы перечисления входят в общий интерфейс библиотеки, они всегда должны использоваться в классе с префиксами, например, как ios::goodbit и ios::io_state.
Для переменной любого типа, для которого определены операции << и >>, цикл копирования записывается следующим образом:
while (cin>>z) cout << z << '\n';
Если поток появляется в условии, то проверяется состояние потока, и условие выполняется (т.е. результат его не 0) только для состояния good(). Как раз в приведенном выше цикле проверяется состояние потока istream, что является результатом операции cin>>z. Чтобы узнать, почему произошла неудача в цикле или условии, надо проверить состояние. Такая проверка для потока реализуется с помощью операции приведения (7.3.2).
Так, если z является символьным вектором, то в приведенном цикле читается стандартный ввод и выдается для каждой строки стандартного вывода по одному слову (т.е. последовательности символов, не являющихся обобщенными пробелами). Если z имеет тип complex, то в этом цикле с помощью операций, определенных в 10.2.2 и 10.2.3, будут копироваться комплексные числа. Шаблонную функцию копирования для потоков со значениями произвольного типа можно написать следующим образом:
complex z; iocopy(z,cin,cout); // копирование complex
double d; iocopy(d,cin,cout); // копирование double char c; iocopy(c,cin,cout); // копирование char
Поскольку надоедает проверять на корректность каждую операцию ввода- вывода, то распространенным источником ошибок являются именно те места в программе, где такой контроль существенен. Обычно операции вывода не проверяют, но иногда они могут завершиться неудачно. Потоковый ввод- вывод разрабатывался из того принципа, чтобы сделать исключительные ситуации легкодоступными, и тем самым упростить обработку ошибок в процессе ввода-вывода.