基于范围的for循环
<p>c++11基于范围的for循环,语法:</p><pre><code class="language-c++">for (Type declaration : expression)
{
// 循环体
}
</code></pre>
<p>在上面的语法格式中<code>Type declaration</code>表示遍历声明,在遍历过程中,当前被遍历导的元素会被存储到声明的变量<code>declaration</code>中。expression是要遍历的对象,它可以是表达式、容器、数组、初始化列表等。</p>
<p>如下代码:</p>
<pre><code class="language-c++">#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
vector<int> t{ 1,2,3,4,5,6 };
for (auto value : t){ //第一次遍历
cout << value++ << " ";
}
cout << endl;
for(int value : t){ //第二次遍历
cout << value << " ";
}
cout << endl;
for(auto& value : t){ //第三次遍历
cout << value++ << " ";
}
cout << endl;
for(auto& value : t){ //第四次遍历
cout << value << " ";
}
cout << endl;
for(const auto& value : t){ //第五次遍历
cout << value << " ";
}
return 0;
}
</code></pre>
<p>运行结果:</p>
<pre><code class="language-c++">1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
2 3 4 5 6 7
2 3 4 5 6 7
</code></pre>
<p>在上面的例子中,第一次遍历是将容器中的元素拷贝到声明的遍历变量value中, 因此无法对value的++操作是不影响原数据的,所以第二次遍历的结果不会有改变。第二次遍历中声明遍历变量用的int,因为这里知道遍历的t容器中全是int类型,auto推导出的就是int类型。</p>
<p>第三次遍历时,遍历变量value声明成引用类型,不仅没有拷贝的过程使得效率更高,而且对value的++操作会直接作用到原数据上,因此第四次遍历的结果会全部+1。</p>
<p>在第五次遍历时,遍历变量声明称const auto&,value被限制成只读权限,如果对value进行++操作会报错。</p>
<h3 id="使用注意">使用注意</h3>
<h5 id="1-关系型容器">1. 关系型容器</h5>
<p>在使用基于范围的for循环遍历map容器时:</p>
<pre><code class="language-c++">#include <iostream>
#include <string>
#include <map>
using namespace std;
int main(void)
{
map<int, string> m{
{1, "lucy"},{2, "lily"},{3, "tom"}
};
// 基于范围的for循环方式
for (auto& it : m)
{
cout << "id: " << it.first << ", name: " << it.second << endl;
}
// 普通的for循环方式
for (auto it = m.begin(); it != m.end(); ++it)
{
cout << "id: " << it->first << ", name: " << it->second << endl;
}
return 0;
}
</code></pre>
<p>上述代码使用了基于范围的for循环方式和普通的for循环方式两种方式对map进行遍历,注意到:</p>
<ul>
<li>
<p>使用普通for循环遍历关系型容器时,auto自动推导出的是一个迭代器类型,需要使用迭代器的类型方式取出元素中的键值对,迭代器返回的是地址:</p>
<p>it->first;</p>
<p>it->second;</p>
</li>
<li>
<p>使用基于范围的for循环方式遍历关系型容器时,auto自动推导出的类型是容器中的value_type,相当于一个<code>std::pair</code>对象,提取键值对的方式:</p>
<p>it.first;</p>
<p>it.second;</p>
</li>
</ul>
<h5 id="2-元素只读">2. 元素只读</h5>
<p>在对基于范围的for循环语法的介绍中可以得知,在for循环内部声明一个变量的引用就可以修改遍历的表达式中的元素的值,但是这并不是用于所有的情况,对应set容器来说,内部元素都是只读的,这是由容器的特性决定的,因此在for循环中auto&会被是为const auto&。</p>
<pre><code class="language-c++">#include <iostream>
#include <set>
using namespace std;
int main(void)
{
set<int> st{ 1,2,3,4,5,6 };
for (auto &item : st)
{
cout << item++ << endl; // error, 不能给常量赋值
}
return 0;
}
</code></pre>
<p>除此之外,在遍历关系型容器map时也会出现同样的问题,基于范围的for循环中,虽然可以得到一个std::pair引用,但是我们是不能修改里面的first值的,也就是key值。</p>
<pre><code class="language-c++">#include <iostream>
#include <string>
#include <map>
using namespace std;
int main(void)
{
map<int, string> m{
{1, "lucy"},{2, "lily"},{3, "tom"}
};
for (auto& item : m)
{
// item.first 是一个常量
cout << "id: " << item.first++ << ", name: " << item.second << endl;// error
}
return 0;
}
</code></pre>
<h5 id="访问次数">访问次数</h5>
<p>基于范围for循环遍历的对象可以是一个表达式或者容器/数组等。在我们对一个容器进行遍历过程中对这个容器的访问次数时一次还是多次呢?</p>
<pre><code class="language-c++">#include <iostream>
#include <vector>
using namespace std;
vector<int> v{ 1,2,3,4,5,6 };
vector<int>& getRange()
{
cout << "get vector range..." << endl;
return v;
}
int main(void)
{
for (auto val : getRange())
{
cout << val << " ";
}
cout << endl;
return 0;
}
</code></pre>
<p>上面代码通过<code>getRange()</code>函数对容器v进行访问,每访问一次就会输出一次<code>get vector range... </code>。</p>
<p>输出结果:</p>
<pre><code class="language-c++">get vector range...
1 2 3 4 5 6
</code></pre>
<p>可以从上面的结果看出只访问了一次,所以不管遍历的容器里面有多少个元素,都只在第一次迭代之前被访问一次,得到这个容器对象之后不会再去重新获取这个对象了。</p>
<p><strong>对应基于范围的for循环来说,冒号后边的表达式只会被执行一次。在得到遍历对象之后会先确定好迭代的范围,基于这个范围直接进行遍历。如果是普通的for循环,在每次迭代的时候都需要判断是否已经到了结束边界。</strong></p><br><br>
来源:https://www.cnblogs.com/ggkx/p/19697670
頁:
[1]