Бесконечный цикл в моем перегруженном операторе >> при проверке ошибок
std::istream & operator >>(std::istream & ins, Rational & target)
{
int num, den;
char symb;
std::cout << "Please enter a rational number: ";
ins >> num >> symb >> den;
std::cout << std::endl;
if(validateInput(num, symb, den)){
target = Rational(num, den);
return ins;
}
else{
std::cin >> target;
}
}
bool validateInput(int num, char symb, int den)
{
if(symb != '/'){
std::cout << "Error: Illegal format. Please use '2/4'." << std::endl;
return false;
}
if((static_cast<int>(num) != num) && (static_cast<int>(den) != den)){
std::cout << "Error: Not a valid rational number." << std::endl;
return false;
}
if(den == 0){
std::cout << "Error: Cannot divide by 0." << std::endl;
return false;
}
return true;
}
Он принимает рациональное число в формате "х / у", например, 2/4. Он работает нормально, если я правильно наберу. Если я введу 2p4, он выдаст правильную ошибку (что мне не хватает '/'), а затем попросит новый номер. Если 0 в знаменателе, он также сообщит об ошибке и запросит новый номер.
Но проверка, чтобы убедиться, что это действительное число, похоже, не работает. Если я введу "a/4", он будет бесконечно зацикливаться, пока не потерпит крах. Я не могу понять, почему. Проверяя отладчик, он возвращается к оператору ins >>, но ничего не запрашивает у пользователя.
Я предполагаю, что моя логика где-то не так. Обратите внимание, я довольно плохо знаком с C++, все еще учусь. Раньше я пытался обрабатывать исключения, но до сих пор не научился этому должным образом, поэтому остановился на чем-то, с чем мне лучше знакомы.
Спасибо!
2 ответа
Основная суть проблемы заключается в том, что операторы извлечения, отформатированные в потоках C++, перестают работать, если состояние потока ухудшается, и вам необходимо сбросить состояние, чтобы они снова работали.
У вас есть и другие проблемы.
Прежде всего, ваша функция проверки показывает отсутствие опыта: static_cast<int>(intval) == intval
всегда будет правдой и ничего не подтверждает. Во-вторых, вы не можете проверить, действительно ли вам удалось извлечь значения из потока (это и есть причина вашего бесконечного цикла: все, что вы делаете, это снова и снова не проверяете).
Итак, когда вы извлекаете значения, вы должны убедиться, что все прошло нормально, например:
int num, den;
char symb;
// Remember to flush unfinished lines
std::cout << "Please enter a rational number: " << std::flush;
if (std::cin >> num >> symb >> den)
// you extracted an integer, a character and an integer succesfully
// perhaps check that the character is '/' and denominator is non-zero
else
// there was an error: what should we do?
Часть "что мы должны делать" далеко не очевидна: вы можете просто сбросить поток и удалить из него первый нарушающий байт и повторить попытку, если считаете, что это разумно (и интуитивно понятно). Однако извлечение может также быть неудачным из-за слишком большого числа, и в этом случае это может привести к странному поведению: рассмотрим следующий ввод (в реализации с общим long
размер):
3111111111111111111111111111111111111111111111111/3
Если у вас нет каких-либо реальных спецификаций, нужно учитывать линейно-ориентированный ввод: сначала прочитайте строку и попробуйте ее проанализировать; если это не выглядит хорошо, проигнорируйте это и попробуйте следующий.
Одним из важных понятий для входных потоков является то, что они не привязаны к какому-либо конкретному устройству; то есть, str >> rational
должен работать, когда str
это консоль, когда это файл, когда это строка, что угодно. Таким образом, экстрактор должен извлечь. Он не должен подсказывать, и не должен объяснять, что ему не нравится. Все эти вещи должны быть сделаны в коде, который вызывает экстрактор.
Проверка в экстракторе - это хорошо, но при обнаружении ошибки обычным способом сообщения о ней является генерирование исключения. Вы можете заставить свои исключения делать все что угодно, удерживая код ошибки или текстовую строку, или имея разные исключения для разных ошибок. Ответственность за то, что код вызывает экстрактор, перехватывает исключения и решает, каков соответствующий ответ.
static_cast<int>(num) != num
поскольку num
это инт, static_cast
ничего не делает Этот тест никогда не подведет.
int main () {try {std:: cout << "Пожалуйста, введите рациональное число: "; Рациональный р; std::cin >> r; } catch(...) { std::cout << "Что-то пошло не так \n"; } }
В целях тестирования я бы переписал эту версию main()
использовать поток строк в качестве источника ввода и инициализировать его "2/4"
или же "2^4"
или что я хотел проверить; это избавило бы от необходимости вводить ввод каждый раз.