第3章 字符串、向量和数组
3.4节迭代器介绍
练习3.21:请使用迭代器重做3.3.3节(第94页)的第一个练习。
【出题思路】迭代器是一种访问容器元素的通用机制,与指针类型类似,迭代器也提供了对对象的间接访问。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。因为本题只需输出vector对象的内容而无须对其进行更改,所以使用的迭代器应该是cbegin和cend,而非begin和end。
【解答】
利用迭代器改写练习3.16所得的程序如下所示:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> v1;
vector<int> v2(10);
vector<int> v3(10, 42);
vector<int> v4{10};
vector<int> v5{10, 42};
vector<string> v6{10};
vector<string> v7{10, "hi"};
cout << "v1的元素分别是:" << v1.size() << endl;
if(v1.cbegin() != v1.cend()) //当vector含有元素时逐个输出
{
cout << "v1的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v1.cbegin(); it != v1.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
cout << "v2的元素分别是:" << v2.size() << endl;
if(v2.cbegin() != v2.cend()) //当vector含有元素时逐个输出
{
cout << "v2的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v2.cbegin(); it != v2.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
cout << "v3的元素分别是:" << v3.size() << endl;
if(v3.cbegin() != v3.cend()) //当vector含有元素时逐个输出
{
cout << "v3的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v3.cbegin(); it != v3.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
cout << "v4的元素分别是:" << v4.size() << endl;
if(v4.cbegin() != v4.cend()) //当vector含有元素时逐个输出
{
cout << "v4的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v4.cbegin(); it != v4.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
cout << "v5的元素分别是:" << v5.size() << endl;
if(v5.cbegin() != v5.cend()) //当vector含有元素时逐个输出
{
cout << "v5的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v5.cbegin(); it != v5.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
cout << "v6的元素分别是:" << v6.size() << endl;
if(v6.cbegin() != v6.cend()) //当vector含有元素时逐个输出
{
cout << "v6的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v6.cbegin(); it != v6.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
cout << "v7的元素分别是:" << v7.size() << endl;
if(v7.cbegin() != v7.cend()) //当vector含有元素时逐个输出
{
cout << "v7的元素分别是:" << endl;
//使用范围for语句遍历每一个元素
for(auto it = v7.cbegin(); it != v7.cend(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
return 0;
}
运行结果:
练习3.22:修改之前那个输出text第一段的程序,首先把text的第一段全都改成大写形式,然后再输出它。
【出题思路】
与原书的示例程序相比,需要将第一段(vector对象第一个空串元素之前的所有元素)改写成大写字母的形式再输出。因为需要更改vector对象的内容,所以使用的迭代器应该是begin和end,而非cbegin和cend。
【解答】
满足题意的程序如下所示:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> text;
string s;
cout << "输入字符串" << endl;
//利用getline读取一句话,直接回车产生一个空串,表示段落结束
while(getline(cin, s))
text.push_back(s); //逐个添加到text中
cout << "text.size = " << text.size() << "输出字符串" << endl;
//利用迭代器遍历全部字符串,遇空串停止循环
for(auto it = text.begin(); it != text.end() && !it->empty(); ++it)
{
//利用迭代器遍历当前字符串
for(auto it2 = it->begin(); it2 != it->end(); ++it2)
{
*it2 = toupper(*it2); //利用toupper改写成大写形式
}
cout << *it << endl; //输出当前字符串
}
return 0;
}
运行结果:
练习3.23:编写一段程序,创建一个含有10个整数的vector对象,然后使用迭代器将所有元素的值都变成原来的两倍。输出vector对象的内容,检验程序是否正确。
【出题思路】
本题与之前题目的区别是由程序自动生成随机数并添加到vector对象中,当输出原始数据时,只需读取而无须更改,所以迭代器选用cbegin和cend;当执行翻倍计算时,需要读写元素内容,所以迭代器选用begin和end。
【解答】
满足题意的程序如下所示:
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
vector<int> vInt;
srand((unsigned)time(NULL)); //生成随机数种子
for(int i = 0; i < 10; ++i) //循环10次
{
//每次循环生成一个1000以内的随机数并添加到vInt中
vInt.push_back(rand() % 1000);
}
cout << "随机生成的10个数据是:" << endl;
//利用常量迭代器读取原始数据
for(auto it = vInt.cbegin(); it != vInt.cend(); ++it)
{
cout << *it << " ";//输出当前数据
}
cout << endl;
for(auto it2 = vInt.begin(); it2 != vInt.end(); ++it2)
{
*it2 *= 2;
cout << *it2 << " ";
}
cout << endl;
return 0;
}
运行结果:
练习3.24:请使用迭代器重做3.3.3节(第94页)的最后一个练习。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vInt;
int iVal;
cout << "请输入一组数字:" << endl;
while(cin >> iVal)
vInt.push_back(iVal);
if(vInt.cbegin() == vInt.cend())
{
cout << "没有任何元素" << endl;
return -1;
}
unsigned long count = vInt.size();
cout << "个数=====" << count << endl;
cout << "相邻两项的和依次是:" << endl;
//如果元素个数是奇数,单独处理最后一个元素
if(0 != (count % 2))
{
//利用auto推断it的类型
for(auto it = vInt.cbegin(); it != vInt.cend() - 1; ++it)
{
//求相邻两项的和
cout << (*it + *(++it)) << " ";
//每行输出5个数字
if((it - vInt.cbegin() + 1) % 10 == 0)
cout << endl;
}
cout << *(vInt.cend() - 1);
}
else
{
//利用auto推断it的类型
for(auto it = vInt.cbegin(); it != vInt.cend(); ++it)
{
//求相邻两项的和
cout << (*it + *(++it)) << " ";
//每行输出5个数字
if((it - vInt.cbegin() + 1) % 10 == 0)
cout << endl;
}
}
return 0;
}
运行结果:
求首尾元素和的程序如下所示:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vInt;
int iVal;
cout << "请输入一组数字:" << endl;
while(cin >> iVal)
{
vInt.push_back(iVal);
}
if(vInt.cbegin() == vInt.cend())
{
cout << "没有任何元素" << endl;
return -1;
}
cout << "首尾两项的和依次是:" << endl;
auto beg = vInt.begin();
auto end = vInt.end();
//利用auto推断it的类型
for(auto it = beg; it != beg + (end - beg) / 2; ++it)
{
//求首尾两项的和
cout << (*it + *(beg + (end - it) - 1)) << " ";
//每行输出5个数字
if(0 == (it - beg + 1) % 5)
cout << endl;
}
//如果元素个数是奇数,单独处理最后一个元素
if(0 != vInt.size() % 2)
cout << *(beg + (end - beg) / 2);
return 0;
}
运行结果:
求解本题时应该特别注意迭代器begin和end的含义,其中begin指向容器的首元素而end指向容器的最后一个元素的下一位置。只有熟悉上述定义才能精确推断迭代器的当前位置在哪里。
练习3.25:3.3.3节(第93页)划分分数段的程序是使用下标运算符实现的,请利用迭代器改写该程序并实现完全相同的功能。
【出题思路】
与指针类似,C++提供了迭代器的算术运算操作,使得迭代器可以在元素间移动;同时我们也可以通过解引用迭代器来获取它所指示的元素,前提是确保迭代器合法。
【解答】
满足题意的程序如下所示:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
//该vector对象记录各分数段的人数, 初始值均为0
vector<unsigned> vUS(11);
auto it = vUS.begin();
int iVal;
cout << "请输入一线成绩(0〜100):" << endl;
while(cin >> iVal)
{
if(iVal < 101) //成绩应在合理范围之内
++*(it + iVal / 10); //利用迭代器定位到对应的元素,加1
}
cout << "您总计输入了 " << vUS.size() << " 个成绩" << endl;
cout << "各分数段的人数分布是(成绩从低到高):" << endl;
//利用迭代器遍历vUS的元素并逐个输出
for(it = vUS.begin(); it != vUS.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
运行结果:
练习3.26:在100页的二分搜索程序中,为什么用的是mid =beg + (end - beg) / 2,而非mid = (beg + end)/2;?
【出题思路】
本题旨在考查迭代器的运算类型及含义。
【解答】
C++并没有定义两个迭代器的加法运算,实际上直接把两个迭代器加起来是没有意义的。
与之相反,C++定义了迭代器的减法运算,两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动多少个元素后可以得到左侧的迭代器,参与运算的两个迭代器必须指向同一个容器中的元素或尾后元素。另外,C++还定义了迭代器与整数的加减法运算,用以控制迭代器在容器中左右移动。
在本题中,因为迭代器的加法不存在,所以mid = (beg +end)/2;不合法。mid = beg+ (end - beg)/ 2;的含义是,先计算end-beg的值得到容器中的元素个数,然后控制迭代器从开始处向右移动二分之一容器的长度,从而定位到容器正中间的元素。