为什么可以用while(cin)?
为什么可以用while(cin)?

/**
* @brief The quick-and-easy status check.
*
* This allows you to write constructs such as
* "if (!a_stream) ..." and "while (a_stream) ..."
*/
operator void*() const
{ return this->fail() ? 0 : const_cast<basic_ios*>(this); }

如果你把一个basic_ios类的对象(cin就是)放到if语句的括号里,它就会被转换成void*型。如果输入失败的话,就会得到一个空指针(也就是0),那么if语句就不能通过。

#include<iostream>
#include<utility>
using namespace std;
int main()
{
int i;
do
{
cout<<i<<endl;
}while(cin>>i);
}

首先输出个0.之后输入X,输出X;当输入ctrl+d时,没有输出,结束。

#include<iostream>
#include<utility>
using namespace std;
int main()
{
int i;
do{
cin>>i;
cout<<i<<endl; }while(cin);
}

输入x,输出x;当输入ctrl+d时,再输出个上一次的输入量,结束。
ctrl+d表示表示输入错误。
cin.clear()可以重新将cin置为有效。
导致循环终止的原因是流对象cin进入错误状态:系统输入级故障;读入了无效数据;遇到文件结束符。
下面是一个用到cin判断作为循环条件的程序:

#include<iostream>
#include<utility>
#include<vector>
#include<map>
using namespace std;
int main()
{
map<string,vector< pair<string,string> > > family;
pair<string,string> pa;
string surName,childName,birthDate;
do{
cout<<"enter surname"<<endl;
cin>>surName;
if(!cin)
break; //如果cin无效,跳出循环
vector< pair<string,string> > child;
pair<map<string,vector<pair<string,string> > >::iterator,bool>
ret = family.insert(make_pair(surName,child));
cout<<"input name and age"<<endl;
while(cin>>surName>>birthDate)
{
pair<string,string> pa;
pa = make_pair(surName,birthDate);
ret.first->second.push_back(pa); //ctrl+d跳出循环,此时cin无效
}
cin.clear(); //使cin重新有效,以达到下面while循环条件。
}while(cin);
cout<<"enter search"<<endl;
cin.clear();
cin>>surName;
map<string,vector< pair<string,string> > >::iterator it = family.find(surName);
if(it==family.end())
cout<<"no this surname"<<endl;
else
{
vector< pair<string,string> >::iterator itt = it->second.begin();
while(itt!=it->second.end())
{
cout<<(*itt).first<<"\t\t"<<(*itt).second<<endl;
itt++;
}
}
return 0;
}

今天看书的时候看到代码while(cin>>val),忽然就在想这样写的合法性是如何判定的。我们都知道cin是一个流对象,而>>运算符返回左边的流对象,也就是说cin>>val返回cin,于是while(cin>>val)就变成了while(cin),问题就变成了一个流对象在判断语句中的合法性。
不管是while(cin)还是if(cin),都是合法的,为什么呢?我们自己定义一个类,然后定义该类的对象,然后使用if语句来判断它是不合法的。这说明,流对象具有某种转换函数,可以将一个流对象转换成判断语句可以识别的类型。
打开iostream.h文件,找到cin的定义,发现是来自于istream.h,其中的模板类basic_istream继承自basic_ios,打开basic_ios的定义,发现它继承自ios_base,再次定位到ios_base类,发现它有两个重载函数。operator void *() const和bool operator!() const。这两个函数使得流对象可作为判断语句的内容。
operator void *() const;函数在while(cin)或是if(cin)时被调用,将流对象转换成void *类型。
bool operator!() const;函数在while(!cin)或是if(!cin)时被调用,将流对象转换成bool类型。
C++中basic_ios.h中的函数定义如下:
- /**
- * @brief The quick-and-easy status check.
- *
- * This allows you to write constructs such as
- * "if (!a_stream) ..." and "while (a_stream) ..."
- */
- operator void*() const
- { return this->fail() ? 0 : const_cast<basic_ios*>(this); }
- bool operator!() const
- { return this->fail(); }
因此,可以简单的理解调用过程为:
while(cin) =====> while(!cin.fail()) //while the stream is OK
while(!cin) =====> while(cin.fail()) //while the stream is NOT OK
需要指出的是,上述两个类型转换都是隐式的。
既然我们找到了while(cin)合法的原因,自然需要试验一下。
我们定义一个类A,在A中定义上述两个函数,然后定义A的一个对象a,使用if(a)和if(!a)来验证一下。代码如下:
- #include<iostream>
- using namespace std;
- class A
- {
- public:
- A(){}
- ~A(){}
- operator void *()const
- {
- cout<<"cast to void*; ";
- return (void *)this;
- }
- bool operator!() const
- {
- cout<<"cast to bool; ";
- return true;
- }
- };
- int main()
- {
- A a;
- if(a) cout<<"first"<<endl;
- if(!a) cout<<"second"<<endl;
- return 0;
- }
运行以上程序,结果为cast to void*; first和cast to bool; second。结果表明,if(a)隐式调用了operator void *()函数,if(!a)隐式调用了bool operator!()函数。
上述两个函数其实是操作符的重载过程。使用这种重载函数,我们就可以像使用cin一样,用if语句对我们的对象做判断了。
为什么可以用while(cin)?的更多相关文章
- cin.ignore()函数的用法
cin.ignore(a,ch)方法是从输入流(cin)中提取字符,提取的字符被忽略(ignore),不被使用.每抛弃一个字符,它都要计数和比较字符:如果计数值达到a或者被抛弃的字符是ch,则cin. ...
- cin的使用问题
#cin的使用问题 cin输入类型不匹配的情况: #include<iostream> using namespace std; void main() { int i = 0; whil ...
- using namespace std 和 using std::cin
相较using std::cin使用using namespace std不会使得程序的效率变低,或者稳定性降低,只是这样作会将很多的名字引入程序,使得程序员使用的名字集合变小,容易引起命名冲突. 在 ...
- while(cin.eof)出错 poj
zoj遇到c++如何判定输入流结尾的问题,一不小心就超时了 楼下的代码可以通过zoj #include<iostream> using namespace std; int main(){ ...
- cin
cin 是预定义的标准输入流对象,cin 用来接收字符串时,遇“空格”.“TAP”.“回车”时都会结束.
- C++输入cout与输出cin
输入和输出并不是C++语言中的正式组成成分.C和C++本身都没有为输入和输出提供专门的语句结构.输入输出不是由C++本身定义的,而是在编译系统提供的I/O库中定义的.C++的输出和输入是用" ...
- cin, cin.getline等函数
char s[100]; cin>>s; // 输入一个字符串,遇“空格”.“TAB”.“回车”都结束 cin.getline(s, 20); // cin.get( ...
- strlen 字符型数组和字符数组 sizeof和strlen的区别 cin.get(input,Arsize)
strlenstrlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值 ...
- C++IO关于cin>>和getline的理解
这个问题困扰了我有一段时间了,趁着十一放假有时间,仔细研究了一下 首先来看一下输入输出运算符cin>>的构成:cin和>> cin>>是由两部分构成的,cin和&g ...
随机推荐
- Java--剑指offer(3)
11.输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. a)使用Integer.toBinaryString(n);来计算得出二进制的字符串,然后使用for循环截取字符串是否为1 pu ...
- android 之 surfaceView和普通View的重绘使用
!自定义控件式需要实现AttrbuteSet 可在xml文件中配置略过创建该对象 普通的View只能在主线程中绘制界面,适用于简单的被动绘制 SurfaceView则可以在新线程中绘制界面,不会阻 ...
- iOS开发小技巧--图片的圆角处理
图片圆角处理方案一: 缺点是图片多了感觉卡 设置图层的CornerRadius属性 -- 设置了圆角后,运行程序没有反应,多半是没有设置下面这句:让内容遵循设置的边缘 masksToBounds = ...
- react.js table组件【可以直接使用】
最近在做一个CMS,使用的技术是刚刚学习的react.js,准备制作一个查询的页面以及一个新增的页面. 这是table的公共组件: 我们在使用的过程中,只会用到: 制作出来的查询页面: 新增页面: 上 ...
- python Chrome 开发者模式消失的方法
最近使用 Chrome浏览器跑Selenium Python 自动化脚本运行过程中,总是出现这样的对话框 出现这样的对话框,如果不能自动关闭,这个对话框会影响web端页面的其他链接的定位识别,这样就 ...
- 【USACO 2.2】Party Lamps
四种开关,n盏灯,1:改变所有灯状态,2:改变奇数灯状态,3:改变偶数灯状态,4:改变3k+1灯状态 给你按开关的总次数c和部分灯限制条件(开或关),一开始都是开着的.($c \leq 10000,n ...
- Android笔试和面试提点
Android基础知识 Android 的四大组件是哪些? Activity,Service,Broadcast和ContentProvide Android 的常用的容器布局是哪些? FrameLa ...
- js-JavaScript高级程序设计学习笔记11
依然第十四章 1.选择部分文本:使用setSelectionRange()方法,两个参数为第一个字符的索引和最后一个字符之后的索引,类似于substring()方法. 2.IE8及更早版本使用范围选择 ...
- MVC5-3 Result分析
众多的Result 使用MVC进行开发,可以看到有ActionResult.ContentReuslt.JsonResult..等,今天对这些Result进行背后分析.它到底是如何做到的 Action ...
- jboss性能优化
jboss linux jboss 部署时优化设置: 在/conf/web.xml中通过参数指定: <session-config> <session-ti ...