关于这一点呢,是在做《C++primer 》关联容器map的一道习题中发现这个蛋疼的问题的。
问题是这样的:
我想要将while循环条件设置为cin,这样就可以不断等待输入,普通的程序可以直接按下ctrl+z中止输入,麻烦在于这里有两层这样的while循环,而ctrl+z会一次性全部退出,导致无法实现想要的目的(在每个外层循环内部,可以输入有限量可中止的内层循环变量,也就是说第一次按下ctrl+z只停止内层的循环,等待进行下一次外层循环,然后再进入内层循环……)
1 #include<<SPAN style="COLOR: black">iostream> 2 #include<<SPAN style="COLOR: black">map> 3 #include<<SPAN style="COLOR: black">string> 4 #include<<SPAN style="COLOR: black">vector> 5 using namespace std; 6 int main() 7 { 8 map<<SPAN style="COLOR: black">string, vector<<SPAN style="COLOR: black">pair<<SPAN style="COLOR: black">string, string>>> family; 9 string family_name; 10 11 while (cin) //输入流状态正常就持续输入,ctrl+z可使其错误而退出 12 { 13 cout << "please input the family name: " << endl; 14 cin >> family_name; 15 16 vector<<SPAN style="COLOR: black">pair<<SPAN style="COLOR: black">string, string>> children; //vector>,用于给family的second赋值 17 pair<<SPAN style="COLOR: black">map<<SPAN style="COLOR: black">string,vector<<SPAN style="COLOR: black">pair<<SPAN style="COLOR: black">string,string>>>::iterator,bool> ret=family.insert(make_pair(family_name, children)); //取insert的返回值 18 19 if (!ret.second) //插入失败,表面之前map里面存在此键 20 { 21 cout << "the family name you give is already exist!" << endl; 22 continue; 23 } 24 25 string child_name, birthday; 26 cout << "please input the children name and birthday: " << endl; 27 28 while (cin >> child_name >> birthday) //输入child_name、birthday来初始化vector 29 { 30 31 ret.first->second.push_back (make_pair(child_name, birthday) ); 32 //ret.first为map>>,->second之后表示vector> 33 } 34 35 cin.clear(); //置流于有效状态,使ctrl+z不会直接退出所有的cin,而只退出上面的cin>>child_name>>birthday 36 } 37 38 39 map<<SPAN style="COLOR: black">string, vector<<SPAN style="COLOR: black">pair<<SPAN style="COLOR: black">string, string>>>::iterator iter_map = family.begin(); //迭代器遍历,输出key对应的value 40 while (iter_map != family.end()) 41 { 42 cout << "family_name: " << iter_map->first << endl; 43 44 vector<<SPAN style="COLOR: black">pair<<SPAN style="COLOR: black">string, string>> vec = iter_map->second; 45 vector<<SPAN style="COLOR: black">pair<<SPAN style="COLOR: black">string, string>>::iterator iter_vec = vec.begin(); 46 47 while (iter_vec != vec.end()) 48 { 49 cout << " "; 50 cout << "children name: " << iter_vec->first << endl; 51 cout << " "; 52 cout << "birthday: " << iter_vec->second << endl; 53 ++iter_vec; 54 } 55 56 ++iter_map; 57 } 58 59 system("pause"); 60 return 0; 61 }
我们来分析一下面临的问题:
将cin >> value (某个变量)放在while的条件中是常见的等待输入的手段,那么如何中止循环呢?
使用ctrl+z组合键,可以中止循环,那么ctrl+z到底是怎么实现的呢?
下面是个人的理解和分析:
原来,ctlr+z是通过将输入流对象的条件状态设置为failbit来使cin>>value为false的,
输入流对象有badbit、failbit、eofbit以及有效等状态,分别对应于硬件错误导致被破坏的流状态、输入不匹配等造成的错误、文件中止导致流的结束、以及有效的流状态。
而通过cin.eof()、cin.fali()、 cin.bad()、 cin.good()的调用可以知道当前流的状态,比如:当前处于failbit状态,则cin.fail()返回true
这里还要介绍一个cin.clear()函数,可以将流状态值重置为有效。
下面用一段测验代码来说明:
#include<<SPAN style="COLOR: black">iostream> using namespace std; int main() { int a; while (cin >> a) { cout << "!" << endl; } //cin.clear(); if (cin.bad()) cout << "bad!" << endl; else if (cin.fail()) //ctrl+z使其fail cout << "fail!" << endl; else if (cin.good()) cout <<"good!" << endl; system("pause"); return 0; }
测验时,在输入几个数字后就按下ctrl+z
若无cin.clear()的调用,输出为fail!
有cin.clear()的调用时,输出为good!
说明,ctrlz+z使流状态变为failbit,而clear可以重置其为有效状态
接下来:
#include<<SPAN style="COLOR: black">iostream> using namespace std; int main() { int a, b; while (cin >> b) { cout << "#" << endl; while (cin >> a) { cout << "!" << endl; } //cin.clear(); if (cin.bad()) cout << "bad!" << endl; else if (cin.fail()) cout << "fail!" << endl; else if (cin.good()) cout << "good" << endl; } if (cin.bad()) cout << "bad!!!!" << endl; else if (cin.fail()) cout << "fail!!!!" << endl; else if (cin.good()) cout << "good!!!" << endl; system("pause"); }
双层while循环嵌套,
这时候,若无cin.clear()
当按下ctrl+z时可以发现,它将直接退出两层循环!
也就是说,ctrl+z可以直接令循环内外的cin的状态均为failbit
而加入cin.clear()代码后
发现,这时候只退出了内层循环,内层循环是goodbit,程序在等待外层的输入,再次按下ctrl+z,发现此时的外层是failbit
(要将缩进应用于写作当中,哈哈哈!)
这样就基本了解清楚了ctrl+z和cin使用的一些内容和注意事项,每次学习遇到这种小细节的时候总是很令人纠结的,但是解决之后会觉得很是神奇,在学习STL 使用的时候解决了一个之前阅读IO 不太仔细的漏洞,这样子解决之后还是很爽的啦!