ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 12) онлайн
Отладка примера с английскими расстояниями
Давайте взглянем на программу, в которой ввод пользователем данных в класс
Distance проверяется на наличие ошибок. Забудем на время про перевод расстоя-
ний из одних единиц в другие и про работу с ними. Пусть наша программа будет
просто получать от пользователя данные в футах и дюймах и выводить их на эк-
ран. Если же пользователь ошибается, нужно отклонить введенные данные, объ-
яснить ему, где он ошибся, и попросить ввести данные заново.
Программа очень проста, за исключением того, что метод getdist() расширен
и направлен на поддержку обработки ошибок. Частично эта программа исполь-
зует подход, показанный в приведенном выше коде. Кроме того, мы еще ввели
выражение, которое следит за тем, чтобы пользователь не вводил футы в формате
чисел с плавающей запятой. Это действительно важно, так как дюймы могут
иметь дробную часть, а футы — нет, и пользователь может слегка запутаться.
Обычно, ожидая ввод целочисленных значений, оператор извлечения просто
прерывает свою работу, увидев точку в десятичной дроби. При этом сообщение
об ошибке не выдается. Мы же хотим узнать о совершенной ошибке, поэтому
будем считывать значение числа футов не в виде int, а в виде строки. Затем мы
проверяем введенную строку при помощи созданной нами функции isFeet(),
возвращающей true, если число футов введено корректно. Для одобрения нашей
функцией введенное число должно содержать только цифры, и его значение
должно лежать в пределах от -999 до 999 (мы просто предполагаем, что более
далекие расстояния измерять не будем). Если с введенными футами все в по-
рядке, конвертируем строку в int с помощью стандартной библиотечной функ-
ции atoi(). Число дюймов вполне может быть дробным. Мы будем проверять
только их значения — они должны лежать в пределах от 0 до 12.0. Кроме того,
будем отслеживать наличие флагов ошибок ios. В общем случае будет установлен
failbit при вводе каких-либо символов вместо числа. Приведем листинг ENGLERR1.
Листинг 12.1. Программа ENGLERR
// englerr.cpp
// контроль ввода данных для класса английских расстояний
#include <iostream>
#include <string>
#include <cstdlib> // для atoi(), atof()
using namespace std;
int isFeet(string); //объявление
///////////////////////////////////////////////////////////
class Distance // Класс английских расстояний
{
private:
int feet;
float inches;
public:
Distance() // конструктор (без аргументов)
{ feet = 0; inches = 0.0; }
Distance(int ft, float in) // конструктор (2 арг.)
{ feet = ft; inches = in; }
void showdist() // вывод расстояния
{ cout << feet << "\'-" << inches << '\"';}
void getdist(); // запросить длину
// у пользователя
};
//---------------------------------------------------------
void Distance::getdist() // получение длины от пользователя
{
string instr; // для входной строки
while(true) // цикл, пока футы
// не будут правильными
{
cout << "\n\nВведите футы: ";
cin.unsetf(ios::skipws);// не пропускать
// разделители
cin >> instr; // получить футы как строку
if( isFeet(instr) ) // правильное значение?
{ //да
cin.ignore(10, '\n'); // съесть символы,
// включая разделитель строк
feet = atoi( instr.c_str() ); // перевести
// значение в целочисленное
break; // выход из цикла 'while'
} // нет, не целое
cin.ignore(10, '\n'); // съесть символы, включая
// разделитель строк
cout << "Футы должны быть целыми < 1000\n";
} //конец цикла while для футов
while(true) // цикл проверки дюймов
{
cout << "Введите дюймы: ";
cin.unsetf(ios::skipws);// не пропускать
// разделители
cin >> inches; // получить дюймы (тип float)
if(inches>=12.0 || inches<0.0)
{
cout << "Дюймы должны быть между 0.0 и 11.99\n";
cin.clear(ios::failbit); //"искусственно"
} // установить флаг ошибки
if( cin.good() ) // все ли хорошо с cin
{ //(обычно вводят не цифры)
cin.ignore(10, '\n'); // съесть разделитель
break; // Ввод корректный, выйти из 'while'
}
cin.clear(); // ошибка; очистить статус ошибки
cin.ignore(10, '\n'); // съесть символы с разделителем
cout << "Неверно введены дюймы\n"; //заново
} //конец while для дюймов
}
//---------------------------------------------------------
int isFeet(string str) // true если введена строка
{ // с правильным значением футов
int slen = str.size(); // получить длину
if(slen==0 || slen > 5) // не было или слишком много данных
return 0; // не целое
for(int j=0; j<slen; j++) // проверить каждый символ
// если не цифра или минус
if((str[j] < '0' || str[j] > '9')&&str[j] != '-')
return 0; // строка неправильных футов
double n = atof( str.c_str() ); // перевод в double
if( n<-999.0 || n>999.0 ) // вне допустимых значений?
return 0; // если да, неправильные футы
return 1; // правильные футы
}
///////////////////////////////////////////////////////////
int main()
{
Distance d; //создать объект Distance
char ans;
do
{
d.getdist(); // получить его значение
cout << "\nРасстояние = ";
d.showdist(); // вывести его
cout << "\nЕще раз (y/n)? ";
cin >> ans;
cin.ignore(10,'\n' ); // съесть символы и
// разделитель
} while(ans != 'n'); // цикл до 'n'
return 0;
}
Мы использовали еще одну хитрость: установили флаг ошибки вручную.
Тем самым мы проверили, входят ли введенные дюймы в диапазон допустимых
значений. Если нет, мы устанавливает failbit с помощью
Мы использовали еще одну хитрость: установили флаг ошибки вручную.
Тем самым мы проверили, входят ли введенные дюймы в диапазон допустимых
значений. Если нет, мы устанавливает failbit с помощью
cin.clear(ios::failbit);//установить failbit
При проверке на наличие ошибок с помощью функции cin.good() флаг failbit
будет обнаружен и выведется сообщение об ошибке.
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48