做题的时候,我经常用while(cin>>a)这样的模式来 实现 连续输入 或 统计输入连续数字的个数 等功能,但是一直都对其中的原理感到迷迷糊糊,我觉得只会用怎么行,还得搞明白 其所以然 来。所以看了一些资料和大佬们的博客,然后写这样一篇博客来解释其中的原理。
使用情形 以及 跳出的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)里面的语句。