做题的时候,我经常用while(cin>>a)这样的模式来 实现 连续输入 或 统计输入连续数字的个数 等功能,但是一直都对其中的原理感到迷迷糊糊,我觉得只会用怎么行,还得搞明白 其所以然 来。所以看了一些资料和大佬们的博客,然后写这样一篇博客来解释其中的原理。
while(cin>>a)的意思是 只要输入的值有效,那么就执行while体内的语句。while循环结束 (跳出流对象)的时候 ,通过检测其流的状态来判断结束: (1)若流是有效的,即流未遇到错误,那么检测成功。 (2)若遇到一个无效的输入时(例如:上述的a是int类型的,但输入的值不是一个整数),istream对象的状态会变为无效,条件就为假。 (3)若遇到文件结束符也可以跳出while(cin>>a)。在windows系统中,输入文件结束符的方法是先按Ctrl+Z,然后再按Enter。在UNIX系统中,包括Mac OS X系统中,文件结束输入为Ctrl+D。 特别的:在while循环中以EOF作为文件结束标志(end of file),EOF是针对文件输入的情况,文件指针如果指向文件尾就会等于EOF。 这种以EOF作为文件结束标志的文件,必须是文本文件!在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
跳不跳出while(cin>>a)循环 关键在于 cin>>a 的值 是不是 假(0)。 运算符返回的是流对象的引用,cin是一个流对象,而>>运算符返回左边的流对象,也就是说cin>>val返回cin,于是while(cin>>val)就变成了while(cin),问题就变成了一个流对象在判断语句中的合法性。 (注意一下:右移运算符>>的结合方向是从左到右 输入流类(istream) 把 >> 重载了) 如果定义了一个类(没有重载 void * 和 ! 运算符的类),然后定义该类的对象,然后使用 while 或者 if 语句来判断它是不合法的。可是为什么while(cin) 和 if(cin)都是合法的呢?原来 输入流类(istream) 重载了 void* 和 ! 这两个运算符!! 打开iostream.h文件,可以看到 operator void *() const和bool operator!() const 这两个函数。这两个函数使得流对象可作为判断语句的内容。 可是重载这两个运算符 跟可作为判断语句的内容有什么关系? 原因是这样的: (以下用if来解释,其实while和if 在处理这方面的情况是一样的) ①先来测试下没有重载上述运算符的情况
#include <iostream> using namespace std; class A { }; int main() { A a; if(a) cout<<"YES"; return 0; }不出所料,报错信息如下:
[Error] could not convert 'a' from 'A' to 'bool'分析:说明编译器编译的时候 不能 将对象a转换为bool类型,不能转换的话 也就无法判断if里判断语句的内容是 真(非0) 是 假(0) 了。 ①改进:重载了void*后
#include <iostream> using namespace std; class A { public: //返回值类型 就是void* 不用额外写 operator void*() { return (void*)0; } }; int main() { A a; if(a) cout<<"YES"; return 0; }分析:这样写后,编译就没问题了。 因为在a不能直接转换为bool类型的情况下,会试图先进行 (void*)a 这样的操作,这个操作会直接调用A类的成员函数operator void*(),这个函数返回一个void*的数据,所以这里的if(a)其实就是if((void*)a),然后根据强制转换后的结果来进行if的判断,这里显然应该不输出。如果改成return (void*)this,那么就可以看到运行后输出了YES ②然后来看看下面的代码
#include <iostream> using namespace std; class A { public: //返回值类型 就是void* 不用额外写 operator void*() { cout<<"a"; return (void*)0; } }; int main() { A a; if(!a) cout<<"YES"; return 0; }输出:aYes 分析:当程序执行到if(!a)时,对于!a,同样是由于a无法直接转化为bool类型,所以!a就等同于!((void*)a),由于(void*)a的值为0 (调用了A类的成员函数operator void*(),然后返回了(void*)0 ),所以!a为真,那么就可以执行if(!a)里面的语句了。 ②改进:重载了 ! 运算符
#include <iostream> using namespace std; class A { public: //返回值类型 就是void* 不用额外写 operator void*() { cout<<"a"; return (void*)0; } bool operator!() { cout<<"A"; return true; } }; int main() { A a; if(!a) cout<<"YES"; return 0; }输出:AYes 分析:重载了运算符!后, !a就直接执行了operator!()函数,该函数返回true,所以可以执行if(!a)里面的语句。