14.2.2. Перегрузка оператора ввода >>
Обычно первый параметр оператора ввода является ссылкой на поток, из которого осуществляется чтение, а второй параметр — ссылкой на некий неконстантный объект, в который предстоит прочитать данные. Обычно оператор возвращает ссылку на свой поток. Второй параметр не должен быть константным потому, что задачей оператора ввода и является собственно запись данных в этот объект.
Оператор ввода класса Sales_data
В качестве примера напишем оператор ввода для класса Sales_data:
istream &operator>>(istream &is, Sales_data &item) {
double price; // инициализировать не нужно; читать в price
// прежде, чем использовать
is >> item.bookNo >> item.units_sold >> price;
if (is) // проверить успех ввода данных
item.revenue = item.units_sold * price;
else
item = Sales_data(); // ввод неудачен: вернуть объект в
// стандартное состояние
return is;
}
За исключением оператора if это определение подобно прежней функции read() (см. раздел 7.1.3). Оператор if проверяет, было ли чтение успешно. Если произойдет ошибка ввода-вывода, он вернет объект Sales_data в состояние пустого объекта. Это гарантирует корректность состояния объекта.
Операторы ввода должны учитывать возможность неудачи ввода, а операторы вывода об этом могут не заботиться.
Ошибки во время ввода
В операторе ввода возможны следующие ошибки.
• Операция чтения может потерпеть неудачу из-за наличия в потоке данных неподходящего типа. Например, после чтения переменной-члена bookNo оператор ввода подразумевает, что следующие два элемента будут числовыми данными. Если во вводе окажутся не числовые данные, поток будет недопустим и все последующее попытки чтения из него потерпят неудачу.
• Во время любой из операций чтения может встретиться конец файла или произойти другая ошибка потока ввода.
Чтобы не проверять каждую часть прочитанных данных, можно проверить состояние потока в целом и только потом использовать прочитанные данные
if (is) // проверить успех ввода данных
item.revenue = item.units_sold * price;
else
item = Sales_data(); // ввод неудачен: вернуть объект в
// стандартное состояние
При сбое любой из операций чтения значение переменной-члена price останется неопределенным. Следовательно, перед ее использованием следует проверить, допустим ли еще поток ввода. Если это так, осуществляется вычисление значения переменной revenue. В случае ошибки ничего страшного не произойдет, поскольку будет возвращен пустой объект класса Sales_data. Для этого объекту item присваивается новый объект класса Sales_data, созданный при помощи стандартного конструктора. После этого присвоения переменная-член bookNo объекта item будет содержать пустую строку, а его переменные члены revenue и units_sold — нулевое значение.
Возвращение объекта в допустимое состояние особенно важно, если объект мог быть частично изменен прежде, чем произошла ошибка. Например, в данном операторе ввода ошибка могла бы произойти уже после успешного чтения в переменную-член bookNo. В результате значения переменных-членов units_sold и revenue останутся неизменными. Таким образом, новое значение bookNo будет связано с данными прежнего объекта.
Оставляя объект в допустимом состоянии, можно в некоторой степени защитить пользователя, который игнорирует возможность ошибки ввода. Объект будет находиться в пригодном для использования состоянии — все его члены окажутся определены. Кроме того, объект не будет вводить в заблуждение — его данные останутся единообразными.
Проектируя оператор ввода, очень важно решить, что делать в случае ошибки и как вновь сделать объект доступным.
Оповещение об ошибке
Некоторые операторы ввода нуждаются в дополнительной проверке данных. Например, оператор ввода мог бы проверить соответствие формату данных, читаемых в переменную bookNo. В таких случаях оператору ввода возможно понадобится установить флаг состояния потока так, чтобы он означал отказ (см. раздел 8.1.2), хотя с технической точки зрения чтение было успешно. Обычно оператор ввода устанавливает только флаг failbit. Флаг eofbit подразумевал бы конец файла, а бит badbit — нарушение потока. Установку этих флагов лучше оставить библиотеке IO.
Упражнения раздела 14.2.2
Упражнение 14.9. Определите оператор ввода для класса Sales_data.
Упражнение 14.10. Опишите поведение оператора ввода класса Sales_data при следующем вводе:
(а) 0-201-99999-9 10 24.95 (b) 10 24.95 0-210-99999-9
Упражнение 14.11. Что не так со следующим оператором ввода класса Sales_data? Что будет при передаче этому оператору данных предыдущего упражнения?
istream& operator>>(istream& in, Sales_data& s) {
double price;
in >> s.bookNo >> s.units_sold >> price;
s.revenue = s.units_sold * price;
return in;
}
Упражнение 14.12. Определите оператор ввода для класса, использованного в упражнении 7.40 раздела 7.5.1. Обеспечьте обработку оператором ошибок ввода.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК