练习 9.3 构成迭代器范围的迭代器有何限制?
1、两个迭代器必须指向同一个容器中的元素,或者是容器最后一个元素之后的位置。
2、end不能再begin之前。
练习9.4 编写函数,接收一对指向vector<int>的迭代器和一个int值。在两个迭代器指定的范围中查找给定的值,返回一个bool值来支出是否找到。
bool judge( vector<int>::iterator left, vector<int>::iterator right, const int target) {
while(left != right){
if(*left == target)
return true;
++left;
}
return false;
}
练习9.5 重写上一题的函数,返回一个迭代器指向找到的元素。注意,程序必须处理未找到给定值的情况。
vector<int>::iterator judge( vector<int>::iterator left, vector<int>::iterator right, const int target) {
while(left != right){
if(*left == target)
return left;
++left;
}
return right ;
}
练习9.6 下面程序有何错误?你应该如何修改它?
list<int> lst1;
list<int>::iterator iter1 = lst1.begin(),iter2 = lst1.end();
while (iter1 < iter2) /* ... */
迭代器不支持 <运算。
练习9.7 为了索引int的vector中的元素,应该使用什么类型?
vector<int>::type_size
练习9.8 为了读取string的list中的元素,应该使用什么类型?如果写入list,又应该使用什么类型?
读取:list<string>::const_iterator;
写入:list<sting>::iterator;
练习9.9 begin和cbegin两个函数有什么不同?
begin 返回容器的迭代器类型,返回iterator 还是const_iterator取决于容器类型,如果容器为const的返回的就是const_iterator,不是const的就返回iterator。
cbegin返回的是const_iterator。
练习9.10 下面4个对象分别是什么类型?
vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();
it1 是iterator。it2是const_iterator。it3是const_iterator。it4是const_iterator。
练习9.11 对于6种创建和初始化vector对象的方法,每一种都给出一个实例。解释每个vector包含什么值。
vector<int> v1{1,2,3,4,5}; //包含1,2,3,4,5 这5个值
vector<int> v2; //空的
vector<int> v3(v1); // 复制了v1的值
vector<int> v4 = v1; //同上一个
vector<int> v5 = {1, 2, 3, 4, 5};// 同 v1
vector<int> v6(v1.begin(),--v1.end());//复制了v1的1,2,3,4
练习9.12 对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的构造函数,解释它们的不同。
对于接受一个容器创建其拷贝的构造函数,对于容器类型和元素类型必须一致。
对于接收两个迭代器所创建拷贝的构造函数,容器类型可以不同,元素类型可以隐式转换即可。
练习9.13 如何从一个list<int>初始化一个vector<double>?从一个vector<int>又该如何创建?编写代码验证你的答案。
list<int> l(10,1);
vector<double> dvec(l.cbegin(),l.cend());
练习9.14 编写程序,将一个list中的char *指针元素赋值给一个vector中的string。
list<const char *> l{"This ","is ","test"};
vector<string> sv;
sv.assign(l.cbegin(),l.cend());
for(auto it:sv)
cout<<it<<endl;
练习9.15 编写程序,判定两个vector<int>是否相等。
vector<int> v1{1,2,3,4,5};
vector<int> v2{1,2,3,6};
if (v1 == v2)
cout<<"v1 = v2";
else cout<<"v1 != v2";
练习9.16 重写上一题的程序,比较一个list<int>中的元素和一个vector<int>中的元素。
vector<int> v1{1,2,3,4,5};
list<int> v2{1,2,3,4,6};
auto it1 = v1.cbegin();
auto it2 = v2.cbegin();
if (v1.size() != v2.size())
cout<<"两个容器中元素不相等";
else {
while(it1 != v1.cend())
if (*it1 == *it2){
++it1;
++it2;
}
else {
cout << "两个容器不相等";
break;
}
if (it1 == v1.cend())
cout << "两个容器相等";
}
练习9.17 假定c1和c2是两个容器,下面的比较操作有何限制?
if(c1 < c2)
两个容器的类型和元素类型必须一致,且不能是无序关联数组。元素类型如果是自定义的类型,必须自己定义了 < 操作。
练习9.18 编写程序,从标准输入读取string序列,存入一个deque中。编写一个循环,用迭代器打印deque的值。
string word;
deque<string> ds;
while(cin>>word)
ds.push_back(word);
deque<string>::const_iterator it1 = ds.cbegin(),it2 = ds.cend();
while(it1 != it2)
cout<<*it1++<<endl;
练习9.19 重写上题的程序,用list代替deque。
string word;
list<string> ds;
while(cin>>word)
ds.push_back(word);
list<string>::const_iterator it1 = ds.cbegin(),it2 = ds.cend();
while(it1 != it2)
cout<<*it1++<<endl;
练习9.20 编写程序,从一个list<int>拷贝元素到两个deque中。值为偶数的所有元素都拷贝到一个deque中,而奇数值元素都拷贝到另一个deque中。
list<int> li = {1,2,3,4,5,6,7,8};
deque<int> di1,di2;
list<int>::const_iterator left = li.cbegin(), right = li.cend();
while(left != right){
if (*left % 2)
di2.push_back(*left++);
else
di1.push_back(*left++);
}
cout<<"di1:";
for (auto temp : di1) {
cout<<temp<<" ";
}
cout<<endl;
cout<<"di2:";
for (auto temp : di2) {
cout<<temp<<" ";
}
cout<<endl;
练习9.22 假定iv是一个int的vector,下面的程序存在什么错误?你将如何修改?
vector<int>::iterator iter = iv.begin(), mid = iv.begin() + iv.size() / 2;
while (iter != mid)
if (*iter == some_val)
iv.insert(iter, 2 * some_val);
错误:iter的值不会发生变换,循环陷入死循环。mid的值在插入后变得非法。
练习9.24 编写程序,分别使用at、下标运算符、front和begin提取一个vector中的第一个元素。在一个空vector上测试你的程序。
vector<int> vec;
cout<<vec.at(0)<<endl;
cout<<vec[0]<<endl;
cout<<vec.front()<<endl;
cout<<*vec.begin()<<endl;
练习9.25 对于第312页中删除一个范围内的元素的程序,如果elem1与elem2相等会发生什么?如果elem2是尾后迭代器,或者elem1和elem2皆为尾后迭代器,又会发生什么?
如果相等,不作任何操作。如果elem2是尾后迭代器,函数行为无定义。elem1和elem2皆为尾后迭代器,不进行操作,然后返回的是尾后迭代器。
练习9.26 使用下面代码定义的ia,将ia拷贝到一个vector和一个list中。使用单迭代器版本的erase从list中删除奇数元素,从vector中删除偶数元素。
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
int ia[] = {0,1,2,3,5,8,13,21,55,89};
vector<int> ivec;
list<int> ili;
for (auto i : ia) {
ivec.push_back(i);
ili.push_back(i);
}
for(auto i: ivec)
cout<<i<<' ';
cout<<endl;
for(auto i: ili)
cout<<i<<' ';
cout<<endl;
vector<int>::iterator it1 = ivec.begin();
list<int>::iterator lit1 = ili.begin(),lit2 = ili.end();
while(it1 != ivec.end()){
if (*it1 % 2 ) {
it1 = ivec.erase(it1);
++lit1;
}
else {
lit1 = ili.erase(lit1);
++it1;
}
}
练习9.27 编写函数,查找并删除forward_list<int>中的奇数元素。
forward_list<int> fil = {1,2,3,4,5,6,7,8,9};
forward_list<int>::iterator prev = fil.before_begin(),curr = fil.begin();
while(curr != fil.end()){
if (*curr % 2 == 1){
curr = fil.erase_after(prev);//删除prev指向的下一个元素,并将prev将要指向的下一个元素指向curr
}else{
prev = curr;
++curr;
}
}
练习9.28 编写函数,接受一个forward_list<string>和两个string共三个参数。函数应在链表中查找第一个string,并将第二个string插入到紧接着第一个string之后的位置。若第一个string未在链表中,则将第二个string插入到链表末尾。
void te(forward_list<string> &fls, string const &s1, string const &s2){
forward_list<string>::iterator curr = fls.begin(), prev = fls.before_begin();
while(curr != fls.end()){
if(*curr == s1){
fls.insert_after(curr,s2);
return;
}
prev = curr++;
}
fls.insert_after(prev, s2);
}
练习9.29 假定vec包含25个元素,那么vec.resize(100)会做什么?如果接下来调用vec.resize(10)会做什么?
vec.resize(100) // 对vec进行扩充,扩充到100个,添加了75个值初始化的元素。
vec.resize(10)//舍弃vec靠后的90个元素。
练习9.31 第316页中删除偶数值元素并复制奇数值元素的程序不能用于list或forward_list。为什么?修改程序,使之也能用于这些类型。
auto remove_evens_and_double_odds(list<int>& data)
{
for(auto cur = data.begin(); cur != data.end();)
if (*cur & 0x1)
cur = data.insert(cur, *cur), advance(cur, 2);
else
cur = data.erase(cur);
}
auto remove_evens_and_double_odds(forward_list<int>& data)
{
for(auto cur = data.begin(), prv = data.before_begin(); cur != data.end();)
if (*cur & 0x1)
cur = data.insert_after(prv, *cur),
advance(cur, 2),
advance(prv, 2);
else
cur = data.erase_after(prv);
}
练习9.32 在第316页的程序中,向下面语句这样调用inset是否合法?如果不合法,为什么?
不合法。
“未指定参数的计算顺序。”因此,在输入函数insert后,iter可能是其原始值或原始值+1,甚至是任何其他值,这取决于编译器的实现方式。
练习9.33 在本节的最后一个例子中,如果不将insert的结果赋予begin,将会发生什么?编写程序,去掉赋值语句,验证你的答案。
会死循环。
练习9.34 假定vi是一个保存int的容器,其中有偶数值也有奇数值,分析下面循环的行为,然后编写程序来验证你的分析是否正确。
vector<int> vi = {1,2,3,4,5,6,7};
auto iter = vi.begin();
while(iter != vi.end()){
if(*iter % 2) {
iter = vi.insert(iter, *iter);
++iter;
}
++iter;
}
for (auto i: vi) {
cout<<i<<' ';
}
之前的那个循环遇到奇数,止步不前,一直在循环,从而跳不出循环。
练习9.35 解释一个vector的capacity和size有何区别。
capacity是指在不重新分配内存的前提下,它最多可以保存多少元素。size是指已经保存的元素数目。
练习9.36 一个容器的capacity可能小于它的size吗?
capacity大于等于size
练习9.37 为什么list或者array没有capacity成员函数?
list是链表,所以每次增加元素时,都会增加相应的内存,array大小固定,不能插入元素,所以size就是array的capacity。
练习9.38:编写程序,探究在你的标准库中,vector是如何增长的。
vector<int> vi;
cout<<"vi.size() = "<<vi.size()<<endl;
cout<<"vi.capacity = "<<vi.capacity()<<endl;
vi = {1,2,3,4,5,6,7};
cout<<"vi.size() = "<<vi.size()<<endl;
cout<<"vi.capacity = "<<vi.capacity()<<endl;
for (int i = 1; i < 20; ++i) {
vi.push_back(i);
cout<<"vi.size() = "<<vi.size()<<endl;
cout<<"vi.capacity = "<<vi.capacity()<<endl;
}
容量是成倍增加的。但是在刚开始的时候,size和capacity是一样的。
练习9.39 解释下面的程序片段做了什么:
vector<string> svec;
sevc.reserve(1024);
string word;
while(cin>>word)
svec.push_back(word);
sevc.resize(svec.size()+svec.size()/2);
开辟一个1024的空间,然后不断往容器里塞东西,等到不塞了,或者是超出容量了,就结束,重新分配一下空间。
练习9.41 编写程序,从一个vector< char >初始化为string
vector<char> c = {'i','o'};
string s(c.begin(),c.end());
练习9.42 假定你希望每次读取一个字符存入一个string中,而且知道最少需要读取100个字符,应该如何提高程序性能?
先reverse 100个空间,然后再一个一个的输入。