Overloading >> operator for Date class in c++ caus

2019-08-30 05:45发布

问题:

I'm trying to overload the >>operator for a Date class in c++ but the it goes in infinite loop when the run goes into the first if statement, can you please help me?

//operator
    istream& operator >>(istream& is,CustomDate& d){
        int day,month,year; 
        char ch1,ch2;
        string test;
        is>>skipws>>day>>ch1>>month>>ch2>>year;
        if(!is){
            is.clear(ios_base::failbit);
            return is;
        }
        if(ch1!='/' || ch2!='/')
            error("invalid date input");
        d = CustomDate(day,month,year);
        return is;
    }

This is the function that calls it

CustomDate Menu::inputDate(){
    CustomDate date;
    cout<<"Input your departure date"<<endl;
    cin>>date;
    if(!cin){
        error("invalid date format");
    }
    return date;
}

And this is the the loop that calls the function

do{
    try{
        date = inputDate();
        validDate = true;
    }
    catch(runtime_error& e){
        cout<<"Date format not valid! must input as dd/mm/yyyy!"<<endl;
        validDate = false;
    }
}while(!validDate);

//customdate constructor
CustomDate::CustomDate()
    :day(1),month(1),year(2012){}

CustomDate::CustomDate(int day, int month, int year)
    :day(day),month(month),year(year){

    if(day<0 || day>30)
        error("Error: Date constructor");
    if(month<0 || month>12)
        error("Error: Date constructor");
    if(year<0)
        error("Error: Date constructor");
}

回答1:

As I said in a comment:

What do you mean by "the clear() function should clear the stream"? It doesn't discard the stream contents, so if there's junk in the stream (such as the character 'a' that can't be parsed as an int) it will never "clear" that junk and will just keep retrying. I think the problem is that clear doesn't do what you think it does.

Rather than throwing an exception from the stream extraction operator just the failbit if it can't extract integers or the separator characters are wrong (also try using more whitespace to help make the code more readable):

istream& operator >>(istream& is, CustomDate& d){
    int day, month, year;
    char ch1, ch2;
    if (is >> day >> ch1 >> month >> ch2 >> year)
    {
        if (ch1 == '/' && ch2 == '/')
            d = CustomDate(day, month, year);
        else
            is.setstate(ios::failbit);
    }
    return is;
}

Then handling failed extractions in inputDate

CustomDate inputDate(){
    CustomDate date;
    cout << "Input your departure date" << endl;
    if (cin >> date)
      return date;

    // something went wrong
    if (cin.eof())
        error("No more data in stream");
    else // cin.fail() is true, probably invalid date format
    {
        // clear error and discard input up to next newline
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        error("invalid date format");
    }
}


回答2:

Your code has a number of errors. The do ... try ... catch loop is potentially an infinite loop, and it is also potentially incorrect.

You'll get an infinite loop if you have enabled exceptions on your input stream and the parse in the stream extraction statement fails in the overloaded operator>>. Your code never resets the stream, so once you get a parse error your code is stuck in a loop. Forever.

What if you don't have exceptions enabled and inputs are mangled enough to make the stream extraction statement mark the stream as "bad" in some way? Exceptions aren't enabled, so no exception is thrown. Your code (the then branch if the if statement immediately after the stream extraction call) will execute. This too doesn't throw an exception. The do ... try ... catch will succeed. Here your code erroneously accepts bad input as valid.



回答3:

I would rework your example as it relies on being able to input multiple items, namely int char int char int. I would input one string. Then use a stringstream to parse the date and check its format.

So it might be something like:

istream& operator >>(istream& is,CustomDate& d){
    string dateStr;
    is>>dateStr;
    istringstream iss(dateStr);
    int day,month,year; 
    char ch1,ch2;
    if(!(iss>>day>>ch1>>month>>ch2>>year)){
        error("invalid date input");
        return is;
    }
    if(ch1!='/' || ch2!='/'){
        error("invalid date format use m/d/y");
        return is;
    }
    d = CustomDate(day,month,year);
    return is;
}

Im not sure if this would fix any infinite loop, but it might be a better way to go for checking the input.