当前位置:编程学习 > C/C++ >>

C++重载输入和输出操作符以及IO标准库中的刷新输入缓冲区残留字符问题

今天在做C++ Primer习题的14.11时,印象中应该挺简单的一题,结果却费了很长时间。
 
类定义:
 
[cpp] 
typedef string Date;  
  
class CheckoutRecord{  
public:  
    CheckoutRecord(){book_id=-1;}  
    friend ostream& operator<<(ostream &os,const CheckoutRecord &obj);  
    friend istream& operator>>(istream &in,CheckoutRecord &obj);  
private:  
    double book_id;  
    string title;  
    Date date_borrowed;  
    Date date_due;  
    pair<string,string> borrower;  
    vector<pair<string,string>*>wait_list;  
};  
 
重载输出操作符很简单:
 
[cpp]  
ostream& operator<<(ostream &os,const CheckoutRecord &obj)  
{  
    os<<"Book ID:"<<obj.book_id<<endl;  
    os<<"Title  :"<<obj.title<<endl;  
    os<<"Data borrowed:"<<obj.date_borrowed<<endl;  
    os<<"Date due     :"<<obj.date_due<<endl;  
    os<<"Borrower     :"<<obj.borrower.first<<" "<<obj.borrower.second<<endl;  
    os<<"Waiters for this book:"<<endl;  
    for(unsigned int i=0;i<obj.wait_list.size();++i)  
        os<<obj.wait_list[i]->first<<" "<<obj.wait_list[i]->second<<";";  
    os<<endl;  
    return os;  
}  
 
重载输入操作符复杂一点,因为我们要考虑用户输入错误的情况:
[cpp]  
istream& operator>>(istream &in,CheckoutRecord &obj)  
{  
    in>>obj.book_id;  
    if(in){  
        //when you typed newline,the new line was added to the   
        //stream buffer, but "in>>obj.book_id" only reads the first double data and   
        // left '\n' character still in the input stream buffer.  
        in.ignore(INT_MAX,'\n');  
        //Title may contain spaces  
        std::getline(in,obj.title);  
  
        in>>obj.date_borrowed>>obj.date_due>>\  
            obj.borrower.first>>obj.borrower.second;  
  
        while(in){  
            pair<string,string> *waiter=new pair<string,string>;  
            in>>waiter->first;  
            if(waiter->first=="end"){  
                delete waiter;  
                break;  
            }  
            in>>waiter->second;  
            obj.wait_list.push_back(waiter);  
        }  
    }  
    else  
        obj=CheckoutRecord();  
    return in;  
}  
 
之所以费了很长时间,主要因为前面少写了一行代码:
[cpp] 
in.ignore(INT_MAX,'\n');  
[cpp]  
导致后面的getline得到的是空行。  
为什么要加上这样一行,我在注释中已经写明了原因。以前一直以为"cin>>whatever_data"这类输入语句,碰到换行或是空白这些分隔符的时候,会在流缓冲中去除有效输入后的下一个分隔符,原来这些分隔符都还在保存在缓冲中!输入语句的行为应该是这样,只会在流中消除在它要的有效输入的前面的分隔符字符,得到想要的输入后,后面又碰到一个分隔符,说明该输入数据结束。但不对想要的输入数据后面的分隔符做任何处理。
不仅是对分隔符,对于输入错误时,"cin>>.."语句在输入终止后,使输入错误的字符让保留在流缓冲区内,这是一定要用cin.ignore来清理。
 
8.2节中的例子:
 
[cpp]  
int ival;  
// read cin and test for only EOF; loop is excuted even if there are other IO failures  
while(cin >> ival, !cin.eof()){  
    if(cin.bad()) // input stream is corrupted; bail out  
        throw runtime_error("IO stream corrupted!");  
    if(cin.fail()){  
        cerr << "Bad data, try again!";  
        cin.clear();  
        cin.ignore();     
        continue;  
    }  
}  
 
不仅是C++的标准IO库,C中的标准IO库也用同样的问题——如果在scanf后面直接调用getline,也会得到空行。而且,对应cin.ignore(),可用fflush(stdin)来刷新缓冲区。
那哪些输入函数会自动处理有效输入后的换行符呢?
 
getline和fgets这类以行为单位的输入函数,会自动将已输入行的换行符从输入缓冲中去除。
 
 
 
这个教训再次告诉我们,使用函数接口的时候,一定要理清它们的行为细节。有时还要弄清它们的底层实现,才能更好地理解它们的行为。
 
 
 
最后给出main函数和测试用例:
 
[cpp]  
/* 
Sample Input: 
 
1001 
Miserable World 
201308 
201309 
Simon Smith 
Mike a 
Kris b 
Tom  c 
Bison d 
Jumping e 
end 
 
err_id 
Miserable World 
201308 
201309 
Simon Smith 
Mike a 
Kris b 
Tom  c 
Bis d 
Jump e 
end 
 
*/  
int _tmain(int argc, _TCHAR* argv[])  
{  
    CheckoutRecord record;  
    cin>>record;  
    cout<<"========================================"<<endl;  
    cout<<record;  
}  
 
 
补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,