范围for语句:
引入的意义:简化传统for的编写,主要用于遍历给定序列中的每个元素并对序列中的每个值执行某种操作,其语法形式是:
for( 声明: 给定序列)
{
执行的操作。
}
其中,“给定序列”部分是一个对象,如一个string的对象。“声明”部分用于定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,“声明”部分的变量会被初始化为“给定序列”部分的下一个元素值。
一个范围for循环的简单例子:
1 string str(“some string”); 2 for(auto c:str)
3 cout << c << endl;
范围for将变量c和str联系起来。str中的字符,每经历一次迭代,就顺序的赋值一个字符给c变量。执行的操作是将变量c输出,这样就输出了str的整个值。
“声明”部分,也可以是引用,如:
1 string str(“some string”); 2 for(auto &c:str) 3 c = toupper(c); 4 cout << str << endl;
对声明部分的c使用引用,从而可以修改str的值,这里在范围for中将str全部大写。
可以尝试使用传统的while和for语句改写,会发现范围for更加简单明了。
列表初始值还是元素数量:
C++11标准引入了{}初始化集合,下面我们总结一下()和{}初始化vector对象的区别。
1 vector<int> v1(10); //v1有10个元素,每个的值都是0。 2 vector<int> v2{ 10 }; //v2有1个元素,该元素的值是10。 3 vector<int> v3(10, 1); //v3有10个元素,每个的值都是1。 4 vector<int> v4{ 10, 1 }; //v4有2个元素,值分别是10和1。
如果用圆括号,可以说提供的值是用来构造(construct)vector对象的,圆括号内的值不全是初始化对象元素的初始值。一般生成具有多个相同数据元素的集合。
如果用花括号,可以表述成我们想列表初始化(list initialize)该vector对象。也就是说,初始化过程会尽可能把花括号内的值当成集合中元素的初始值来处理。
另一方面,当花括号内的值不能执行列表初始化,编译器会尝试使用构造vector对象,即这个时候花括号和圆括号的功能一样,如示例v7,v8的初始化:
1 vector<string> v5{ "hi" }; //列表初始化,v5有一个元素。 2 vector<string> v6("hi"); //错误:不能用字符串字面值构建vector对象。 3 vector<string> v7{ 10 }; //v7有10个默认初始化的元素。因为10不是string类型,不能列表初始化该对象。 4 vector<string> v8{ 10, "hi" }; //v8有10个值为"hi"的元素。
标准库函数begin和end:
C++在遍历数组时,很容易出错,为了让指针的使用更简单、安全。C++11新引入了两个名为begin和end的函数。这两个函数与vector容器中的两个同名方法功能类似。不过数组毕竟不是类类型,因此这两个函数不是成员函数。正确的使用形式是将数组作为它们的参数:
1 int ia[] = { 0,1,2,3,4,-5,6,7,8,9 }; 2 int *beg = begin(ia); //指向ia首元素的指针。 3 int *last = end(ia); //指向ia尾元素的下一个位置的指针。
使用begin和end可以很容易地写出一个循环并处理数组中的元素。例如:
1 //寻找第一个负值元素。 2 while (beg != last && *beg >= 0) 3 ++beg;
另外,还可以使用数组的指针来初始化vector:
1 int int_arr[] = { 0, 1, 2, 3, 4, 5 }; 2 //ivec有6个元素,分别使int_arr中对应元素的副本 3 vector<int> ivec(begin(int_arr), end(int_arr));