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

今天在作C++ Primer习题的14.11时,印象中应该挺简单的一题,结果却费了很长时间。函数

类定义:oop

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;
};

重载输出操做符很简单:测试

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;
}


重载输入操做符复杂一点,由于咱们要考虑用户输入错误的状况:

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;
}

之因此费了很长时间,主要由于前面少写了一行代码:
in.ignore(INT_MAX,'\n');
致使后面的getline获得的是空行。

为何要加上这样一行,我在注释中已经写明了缘由。之前一直觉得"cin>>whatever_data"这类输入语句,碰到换行或是空白这些分隔符的时候,会在流缓冲中去除有效输入后的下一个分隔符,原来这些分隔符都还在保存在缓冲中!输入语句的行为应该是这样,只会在流中消除在它要的有效输入的前面的分隔符字符,获得想要的输入后,后面又碰到一个分隔符,说明该输入数据结束。但不对想要的输入数据后面的分隔符作任何处理
this

不只是对分隔符,对于输入错误时,"cin>>.."语句在输入终止后,使输入错误的字符让保留在流缓冲区内,这是必定要用cin.ignore来清理。spa

8.2节中的例子:code

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这类以行为单位的输入函数,会自动将已输入行的换行符从输入缓冲中去除。ci

 

这个教训再次告诉咱们,使用函数接口的时候,必定要理清它们的行为细节。有时还要弄清它们的底层实现,才能更好地理解它们的行为get

 

最后给出main函数和测试用例:input

/*
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;
}