《C++ Primer》第九章 知识点练习题答案(原创)
9.1 顺序容器概述
1.几种顺序容器的特点
vector:可变大小,支持快速随机访问,尾部插入/删除数据很快
deque:双端队列。支持快速随机访问,头尾插入/删除数据很快
list:双向链表。支持双向顺序访问,在其任何位置插入删除数据都很快
array:固定大小数组,不能改变大小。(注意这里与普通的内置数组类型是不一样的)
- string:与vector类似,专用于保存字符。
forward_list 单向链表。仅支持单向顺序访问,任何位置插入删除都很快。
2.通常,使用vector是最好的选择,除非有很好的理由选择其他容器。(如中间插入,随机访问,额外开销)
9.1:
(a):list 因为涉及中间插入
(b):deque 因为涉及头尾的增删
(c)vector 因为无特殊要求。
9.2 容器库概览
- 顺序容器几乎可以保存任意类型的元素,但某些容器操作对元素类型有其自己的特殊要求。
eg: vector<vector> -->string的vector
9.2:list<deque
9.21 迭代器
1.迭代器范围:由一对迭代器表示,分别指向同一个容器中的元素或尾后元素。
[begin,end),即begin开始,end前结束。
应用:begin==end,此时为空。
while(begin!=end)
{
*begin=val;
++begin;
}
9.3:限制:一对迭代器指向同一个容器或者最后一个元素之后的位置.
9.4:
#include <iostream>
#include <vector>
using namespace std;
bool find1(vector<int>::iterator a,vector<int>::iterator b,int c)
{
for(a;a!=b;a++)
{
if((*a)==c)
return true;
}
return false;
}
int main()
{
vector<int> v(10,1); //10个1
cout<<find1(v.begin(),v.end(),1)<<endl;
cout<<find1(v.begin(),v.end(),10);
}
1
0
9.5:
find1改为vector
9.6: List中未重载非==,!=的,故不能直接比较。
9.2.2 容器类型成员
容器常用类型别名:
- iterator 迭代器
- size_type 无符号整数类型,足够保存此容器类型最大可能容器大小
- difference_type 带符号整型,足够保存两个迭代器之间的距离
- reference 左值引用
value_type 元素类型
9.7 vector::size_type
9.8 写不能用const iterator.9.2.3 begin和end成员
- 当不需要写操作时,应使用cbegin和cend.
auto可与begin和end结合,此时获得的迭代器取决于容器类型。(是否const)
auto it1=a.begin();//仅当a是const时,it1是const_iterator
9.9 cbegin返回const_iterator
9.10
it1:vector
it2: vector
it3:vector
it4:vector
9.2.4 容器定义和初始化
- 每个容器类型都定义了一个默认构造函数,除array外,其他容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值的参数。
- 将一个容器拷贝有两种方法:可直接拷贝(容器类型和元素类型必须匹配),或者用迭代器参数来拷贝一个范围(此时不要求容器类型相同,且两容器中的元素类型也可以不同,只要能进行转换(如char*->string)),注意此时的范围仍然是左闭右开。
- 列表初始化:A a{,,,}
- 顺序容器还提供另一个构造函数:
A a(10,-1) //10个-1
A a(10) //10个默认值 - 当定义一个array时,除了指定元素类型,还要指定大小
array<int,10>i;
arrat<string,10>::size_type j; - array赋值时初始值只能小于等于固定大小。
array支持拷贝赋值。
9.11vector<int> vec;// 0 </br>vector<int> vec(10);// 0 </br>vector<int> vec(10,1); // 1 </br>vector<int> vec{1,2,3,4,5}; // 1,2,3,4,5 </br>vector<int> vec(other_vec); // same as other_vec </br>vector<int> vec(other_vec.begin(), other_vec.end()); //same as other_vec
9.12 接受迭代器创建拷贝,拷贝的是两个迭代器之间的内容,而直接接受一个容器的拷贝,拷贝整个容器的内容。
9.13 用迭代器范围拷贝 ;直接拷贝即可9.2.5 赋值和swap
- 赋值要求左右运算对象具有相同类型。
- 顺序容器定义了assign成员提供不同单相容的类型赋值。(array除外)
swap将容器内容交换不会导致指向容器的迭代器、引用和指针失效(array和string会)
9.14list<char*> l1; vector<string> v1; v1.assign(l1.cbegin(),l1.cend());
9.2.6 容器大小操作
每个容器有三个与大小相关操作:size(返回容器数目),empty(size为0时返回true),max_size(返回大于或等于容器所能容纳最大元素书的值)。其中仅forward_list不支持size。
9.2.7 关系运算符
- 每个容器都支持相等运算符(==,!=);除了无需关联容器外所有容器都支持关系运算符(>、>=、<、<=).关系运算符两边运算对象必须是相同类型的容器且保存相同类型的元素。
若两容器相等,则一定是大小相等且元素一一对应。大小不等,则根据第一个不等元素进行比较。
9.15 长度相等:一一比较对应元素。不等则两容器不等
9.16 比较的是元素是否相等。
9.17 必须容器类型和元素类型都相等。9.3 顺序容器操作
9.3.1 向顺序容器中添加元素
- push,push_back,push_front:传递元素类型的对象,这些对象被拷贝到容器中。
- emplace,emplace_back,emplace_front:讲参数传递给元素类型的构造函数。
insert,emplace,都是插入到指定元素之前的一个元素上。
9.18
deque<string>ds;
string buf;
while(cin>>buf)
ds.push_back(buf);
for(auto i1=ds.begin();i1!=ds.end();i1++)
cout<<*i1<<endl;
9.19 改下类型即可。
9.20
deque<int>d_1,d_2;
list<int>l1={1,2,3,4,5,6};
for(const auto a:l1)
{
if(a%2==0)
d_2.push_back(a);
else
d_1.push_back(a);
}
9.21
首先定义指向vector首地址的迭代器,当使用insert后,元素将被插入到迭代器所指元素之前,然后将此时返回的新的首元地址赋值重新赋值给迭代器,以便后续元素插入。
9.22 此时由于insert返回了新的迭代器首部且改变了size,将导致mid迭代器失效,从而无限循环。
vector<int>::iterator iter=iv.begin();
while(iter!=iv.begin()+iv.size()/2)
{
if(*iter==some_val)
{
iter=iv.insert(iter,2*some_val);
iter+=2;
}
else ++iter;
}
9.3.2 访问元素
c.back()/front() :返回尾/首元素的引用,其中back不适用于forward_list
c[n]:快速访问,不提供越界异常
c.at(n) 快速访问,提供越界异常
eg:
if(!c.empty)
{
c.front()=45; //赋值第一个元素
auto &v=c.back();
v=1024;//改变
auto v2.c.back();
v2=0; //未改变
}
9.23 均为第一个元素
9.24
vector<int> vec1;
int a = vec1.front();//terminating with uncaught exception of type std::out_of_range
int b = vec1[0];// Segmentation fault: 11
int c = vec1.at(0);
int d = *vec1.begin();//需要解引用
9.3.3 删除元素
c.pop_back() 删除尾元素,forward_list不支持。
c.pop_front() 删除首元素,vector、string不支持
c.erase(p) 删除迭代器p所指元素,返回被删元素之后元素的迭代器
c.erase(b,e) 删除迭代器b和e范围内的元素,返回一个指向最后一个被删元素之后的迭代器。(即此时b==e)
c.clear() 删除所有元素,返回void
9.25 相等:不发生删除操作
elem2为尾后迭代器:删除elem1到最后一个元素
9.26
int ia[]={0,1,2,3,5,8,13,21,55,89};
vector<int> vi(ia,ia+(sizeof(ia)/sizeof(*ia)));
auto iter=vi.begin();
while(iter!=vi.end())
{
if(*iter%2)
iter=vi.erase(iter);
else iter++;
}
list同理。
9.3.4 特殊的forward_list操作
- lst.before_begin() 返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。
- lst.insert_after(p,t)/(p,n,t) 在p之后的位置插入t,n是数量
- lst_insert_after(p,b,e) b和e是表示范围的一对迭代器(不能指向lst内)
- lst_insert_after(p,il) il是花括号列表。
以上返回为指向最后一个插入元素的迭代器。 - emplace_after(p,args) 使用args在p之后创建一个元素。返回指向这个新元素的迭代器
- lst.erase_after(p)/(b,e) 删除p指向元素之后的元素或b之后e之前的元素。 返回被删元素之后的迭代器。
9.27
forward_list<int> fi{1,2,3,4,5};
auto pre=fi.before_begin();
auto cur=fi.begin();
while(cur!=fi.end())
{
if(*cur%2)
cur=fi.erase_after(pre);
else
{
pre=cur;
cur++;
}
}
9.28
bool flag=false;
forward_list<int> fi;
auto pre=fi.before_begin(),cur=fi.begin();
for(;cur!=fi.end();pre=cur++)
{
if(*cur==string1)
{
a.insert_after(pre,string2);
flag=true;
}
}
if(!flag)
a.insert(pre,string2);
9.3.5 改变容器大小
c.resize(n)/(n,t) 调整大小为n个元素。若n<c.size,多出的元素被丢弃;否则初始化新元素并加入。
9.29 添加75个0进去; 去除15个元素。
9.30 必须提供默认构造函数。
9.3.6 容器操作可能使迭代器失效
- 使用迭代器时,要考虑改变容器后迭代器、指针和引用是否失效。
不要保存end返回的迭代器,因为它很可能会失效。
9.31 ++操作不能用于list、forward_list .可改为advance(iter,2)
9.32 不合法。此时之后的操作会无限循环的插入1。
9.33 程序会崩溃,因为begin迭代器的值在插入一个元素之后已经失效。
9.34 此时也会无限循环。应iter+=2.9.4 vector对象是如何增长的
- shrink_to_fit:请将capacity()减少为size大小。只适用于vector,string,deque.
- c.capacity 不重新分配内存空间的话,c可以保存多少元素
c.reserve(n) 分配至少能容纳n个元素的内存空间。
需要注意,reserve并不改变容器中元素数量,它仅影响vector预先分配的空间。
只有在不得已时才会分配新的内存空间。
9.35 capasity是预留空间大小,size是当前已使用的大小。
9.36不可能,至少相等。
9.37 list是链表,储存元素内存是不连续的,所以不需要capasity.而array是固定好的。
9.38vector<int> vec1; int a; while (cin>>a) { vec1.push_back(a); cout<<"vector的大小"<<vec1.size()<<endl;; cout<<"vector的容量"<<vec1.capacity()<<endl; }
9.39 首先reserve足够空间,等向vector添加元素完自动添加size/2个初始值。
9.40 不同编译器会有不同结果。9.5 额外的string操作
9.5.1 构造string的其他方法