最后总结:这是一个“不了解cin工作原理”而导致的问题。把cin看做指针,从缓冲区逐位读取数据。
问题背景:所用教材《C++ Primer》第五版,P94 3.17练习题,从cin读入一组词并输入一个vector对象中,再把所有词改为大写形式,输出结果,每个单词单独一行显示。本次准备改进一下,程序以输入回车结束。
主程序如下:
int main()
{
vector<string>A;
string B;
int N=0;
while(cin)
{
if(cin.get()=='\n')
break;
else
{
cin>>B;
A.push_back(B);
N++;
}
}
for(int i=0;i<=N;i++)
{
for(auto &a:A[i])
{
a=toupper(a);
}
cout<<A[i]<<endl;
}
return 0;
}
运行后:
把程序更改为如下:
int main()
{
vector<string>A;
string B;
int N=0;
while(cin)
{
cin>>B;
A.push_back(B);
N++;
if(cin.get()=='\n')
break;
}
for(int i=0;i<=N;i++)
{
for(auto &a:A[i])
{
a=toupper(a);
}
cout<<A[i]<<endl;
}
return 0;
}
运行结果符合预期:
原因正在分析中。
———————————————————————————————————————————
原因分析:
首先while(cin>>A)实际上是
int main()
{
int A;
while(cin)
{
if(cin>>A)
cout<<A<<endl;
else
break;
}
return 0;
}
即while(cin>>A)和while(cin)是等价的,检测的对象都是流的状态,即cin里边有没有东西,只要有东西就进入循环,若cin里边的内容和A同一类型,则进行循环中的下一步操作;如不是同一类型跳出循环,如图1.1,图1.2和图1.3,当碰见无效输入(和A类型不一致的输入)跳出循环,执行下一语句。所以while(cin)还是while(cin>>A)不会导致程序忽略第一个字符,但是会出现程序不输出最后一个元素的新问题,这个问题见下一篇“不输出最后一个元素”文章。
#include<iostream>
using namespace std;
int main()
{
int A;
while(cin>>A)
cout<<A<<endl;
return 0;
}
图1.1
图1.2
图1.3
其次,键盘的输入内容先放在缓冲区,当输入完成后按下回车,cin再从缓冲区读入。此时,这个回车就具有两个功能了,一个是表示输入完成,cin开始读入;另一个是自己设定的循环结束标志,这会不会出问题呢?这就是第二个踩坑记录,部分程序最后一个元素不输出。
最后,总结以上经验,cin.get()检测完成后会扔掉所检测的字符,它不参与后续操作。即aaa中第一个a用于检测,检测完成后扔掉,用后边的。
我们输入aaa bbb ccc,第一个a检测完成后扔掉,只有aa进入了字符串B。紧接着的这个空格,触发了两个操作,一个是字符串碰见空白会停止,完成输入,得到第一个字符串aa,返回循环开头,空格也算输入,cin为真,循环再次;第二个操作就是空格进行了cin.get()检测,检测完成后被扔掉,bbb输入字符串,以此类推。综上所述,第一个a被cin.get()检测,用完扔了,所以输出aa;之后的检测用到的是空格,所以bbb,ccc能完整的输出。
验证:输入 (空格)aaa(空格)bbb(空格)ccc 。结论成立。
图1.4