专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!
目录
- 5.4迭代语句
- 5.4.1 while语句
- 传统的for语句
- 传统for循环的执行流程
- for语句头中的多重定义
- 范围for语句
- do while语句
5.4迭代语句
迭代语句通常称为循环,它重复执行操作直到满足某个条件才停下来。while和for语句在执行循环体之前检查条件,do while语句先执行循环体,然后再检查条件。
5.4.1 while语句
只要条件为真,while语句(while statement)就重复地执行循环体,它的语法形式是
while(condition)
statement
在while结构中,只要condition的求值结果为真就一直执行statement(常常是一个块)。condition不能为空,如果condition第一次求值就得false,statement一次都不执行。
while的条件部分可以是一个表达式或者是一个带初始化的变量声明。通常来说,应该由条件本身或者是循环体设法改变表达式的值,否则循环可能无法终止。
使用while循环
当不确定到底要迭代多少次时,使用while循环比较合适,比如读取输入的内容就是如此。还有一种情况也应该使用while循环,这就是我们想在循环结束后访问循环控制变量。例如:
vector<int> v;
int i;
//重复读入数据,直至到达文件末尾或者遇到其他输入问题
while(cin>>i)
v.push_back(i);
//寻找第一个负值元素
auto beg=v.begin();
while(beg!=v.end()&&*beg>=0)
++beg;
if(beg==v.end())
//此时我们知道v中的所有元素都大于等于0
第一个循环从标准输入中读取数据,我们一开始不清楚循环要执行多少次,当cin读取到无效数据、遇到其他一些输入错误或是到达文件末尾时循环条件失效。第二个循环重复执行直到遇到一个负值为止,循环终止后,beg或者等于v.end(),或者指向v中一个小于0的元素。可以在while循环外继续使用beg的状态以进行其他处理。
传统的for语句
for语句的语法形式是
for (init-statement ;condition; expression){
statement
}
关键字for及括号里的部分称作for语句头。init-statement必须是以下三种形式中的一种:声明语句、表达式语句或者空语句,因为这些语句都以分号作为结束,所以for语句的语法形式也可以看做
for (initializer:condition; expression)
statement
一般情况下,init-statement负责初始化一个值,这个值将随着循环的进行而改变。condition作为循环控制的条件只耍condition为真,就执行一次statement 如果condition 第一次的求值结果就是false,则stalement一次也不会执行。expression负责修改 init-statement初始化的变量,这个变量正好就是condition检查的对象,修改发生在每次循环逄代之后。statement可以是一条单独的语句也可以是一条复合语句。
传统for循环的执行流程
//重复处理s中的字符直至我们处理完全部字符或者逼到了一个表示空白的字符
for(decltype(s.size())index=0;
index!=s.size()&&!isspace(s[index]);++index)
s[index]=toupper(s[index]);//将当前字符改成大写形式
求值的顺序如下所示:
- 循环开始时,首先执行一次init-statement。此例中,定义index并初始化为0。
- 接下来判断condition,如果index 不等于s.size,而且在s[index]位置的字符不是空白,则执行for循环体的内容。否则,循环终止。如果第一次迭代时条件为假,for循环体一次也不会执行。
- 如果条件为真,执行循环体。此例中,for循环体将s[index]位置的字符改写成大写形式。
- 最后执行expression。此例中,将index的值加1。
这4步说明了for循环第一次迭代的过程。其中第1步只在循环开始时执行一次,第2、3、4步重复执行直到条件为假时终止,也就是在s中遇到一个空白字符或者index大于s.size()时终止。
牢记for语句头中定义的对象只在for循环体内可见.因止在上面的例子中,for循环结束后index就不可用了。
for语句头中的多重定义
和其他的声明一样,init-statement也可以定义多个对象。但是init-statetment只能有一条声明语句,因此,所有变量的基础类型必须相同。举个例子,我们用下面的循环把vector的元素拷贝一份添加到原来的元素后面:
//记录下的大小,当到达原来的最后一个元素后结束循环
for(decltype(v.size()) i=0,sz=v.size();i!=sz;++i)
v.push_back(v[i]);
在这个循环中,我们在init-statement里同时定义了索引i和循环控制变量sz。省略for语句头的某些部分何for语句头能省略掉init-statement,condition和expression中的任何一个(或者全部)。
如果无须初始化,则我们可以使用一条空语句作为init-statement。例如,对于在vector对象中寻找第一个负数的程序,完全能用for循环改写:
auto beg=v.begin();
for(/*空语句*/;beg!=v.end()&&*beg>=0;++beg)
;//什么也不做
注意,分号必须保留以表明我们省略掉了init-statement。说得更准确一点,分号表示的是-个空的init-stafement。在这个循环中,因为所有要做的工作都在for语句头的条件和表达式部分完成了,所以for循环体也是空的。其中,条件部分决定何时停止查找,表达式部分递增迭代器。
省略condition的效果等价于在条件部分写了一个true。因为条件的值永远是true,所以在循环体内必须有语句负责退出循环,否则循环就会无休止地执行下去:
for(int i = 0;/*条件为空*/;++i){
//对i进行处理,循环内部的代码必须负责终止选代过程!
}
我们也能省略掉for语句头中的expression,但是在这样的循环中就要求条件部分或者循环体必须改变迭代变量的值。举个例子,之前有一个将整数读入vector的while循环,我们使用for语句改写它:
vector<int> v;
for(int i;cin>>i;/*表达式为空*/)
v.push_back(i);
因为条件部分能改变i的值,所以这个循环无须表达式部分。其中,条件部分不断检查输入流的内容,只要读取完所有的输入或者遇到一个输入错误就终止循环。
范围for语句
C++11新标引入了一种更简单的for语句,这种语句可以遍历容器或其他序列的所有元素。范围for语句(range for statement)的语法形式是:
for(declaration:expression)
statement
expression表示的必须是一个序列,比如用花括号括起来的初始值列表、数组或者vector或string等类型的对象,这些类型的共同特点是拥有能返回迭代器的begin和end成员。
declaration定义一个变量,序列中的每个元素都得能转换成该变量的类型。确保类型相容最简单的办法是使用auto类型说明符,这个关键字可以令编译器帮助我们指定合适的类型。如果需要对序列中的元素执行写操作,循环变量必须声明成引用类型。
每次迭代都会重新定义循环控制变量,并将其初始化成序列中的下一个值,之后才会执行statement。像往常一样,statement可以是一条单独的语句也可以是一个块。所有元素都处理完毕后循环终止。
之前我们已经接触过几个这样的循环。接下来的例子将把vector对象中的每个元素都翻倍,它涵盖了范围for语句的几乎所有语法特征:
vector<int> v={0,1,2,3,4,5,6,7,8,9};
//范围变量必须是引用类型,这样才能对元素执行写操作
for(auto&r:v)// 对v中的每一元素
r*2;//将v中每个元素的值翻倍
for语句头声明了循环控制变量r,并把它和v关联在一起,我们使用关键字auto后编译器为r指定正确的类型。由于准备修改v的元素的值,因此将z声明成引用类型。此时,在循环体内给r赋值,即改变了z所绑定的元素的值。
范围for语句的定义来源于与之等价的传统for语句:
for(auto beg=v.begin(),end=v.end();beg!=end;++beg){
auto&r=*beg;//r必须是引用类型,这样才能对元素执行写操作
r*=2;//将中每个元素的值翻倍
}
在范围for语句中,预存了end()的值。一旦在序列中添加(删除)元素,end函数的值就可能变得无效了。
do while语句
do while 吾句(do while statement)和while语句非常唯一的区别是,do while语句先执行循环体后检查条件。不管条件的值如何,我们都至少执行一次循环。do while 语句的语法形式如下所示:
do
statement
while(condition);
do while语句应该在括号包围起来的条件后面用一个分号表示语句结束
在do语句中求condilion的值之前首先执行次statement,condition 不能为空。如果condition的值为假,循环终止;否则,重复循环过程。condition使用的变量必须定义在循环体之外。
我们可以使用do while循环(不断地)执行加法运算:
//不断提示用户输入一对数,然后求其和
string rsp;//作为循环的条件,不能定义在do的内部
do{
cout<<"please enter two values:";
int val1=0,val2=0;
cin>>val1>>val2;
cout<<"the sumo f"<<val1<<" and "<<val2 <<" = " << val1+val2 <<"\n\n" << " more entery yes or no:";
cin >> rsp;
while( rsp.empty()&&rsp[0]!='\n');
}
循环首先提示用户输入两个数字,然后输出它们的和并询问用户是否继续。条件部分检查用户做出的回答,如果用户没有回答,或者用户的回答以字母n开始,循环都将终止否则循环继续执行。
因为对于do while来说先执行语句或者块,后判断条件,所以不允许在条件部分定义变量:
do{
//...
mumble(foo);
}while(int foo=get_foo());//错误:将变量声明放在了do的条件部分
如果允许在条件部分定义变量,则变量的使用出现在定义之前,这显然是不合常理的!