Когда вы вставляете букву, это происходит:
operator>>
извлекает символы из потока и пытается преобразовать их в число; - при преобразовании происходит сбой, поэтомуустанавливает состояние потока на
ios::failbit
и возвращает;opt
, вероятно, не тронут (стандарт делегирует этот материал в библиотеку локалей, которая является зоной C ++, которую я никогда не понимал - для достаточно смелых, это в §22.2.2.1.2);так как он вернулся и (вероятно) opt
остался как есть, цикл продолжается; - когда выполнение возвращается к
std::cin >> opt;
, operator>>
видит, что состояние все еще ios::failbit
, поэтомудаже не пытается извлечь что-либо; - Перейти к 3.
Чтобы исправить проблему, вы должны очистить состояние ошибки и удалить «неправильные» символы из буфера ввода.Поскольку вы, вероятно, не хотите добавлять весь этот код к каждому cin>>
, полезно создать функцию для решения этой распространенной проблемы;лично я создал этот маленький заголовок (AcquireInput.hpp
), который оказался полезным много раз:
#ifndef ACQUIREINPUT_HPP_INCLUDED
#define ACQUIREINPUT_HPP_INCLUDED
#include <iosfwd>
#include <limits>
#include <string>
template<typename InType> void AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString, InType & Result)
{
do
{
Os<<Prompt.c_str();
if(Is.fail())
{
Is.clear();
Is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Is>>Result;
if(Is.fail())
Os<<FailString.c_str();
} while(Is.fail());
}
template<typename InType> InType AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString)
{
InType temp;
AcquireInput(Os,Is,Prompt,FailString,temp);
return temp;
}
/* Usage example:
//1st overload
int AnInteger;
AcquireInput(cout,cin,"Please insert an integer: ","Invalid value.\n",AnInteger);
//2nd overload (more convenient, in this case)
int AnInteger=AcquireInput(cout,cin, "Please insert an integer: ","Invalid value.\n");
*/
#endif