概述
第九章 顺序容器
一个容器就是一些特定类型对象的集合。顺序容器为程序员提供了元素存储和访问顺序的能力,这种顺序不依赖于元素的值,只与元素加入容器时的位置相对应。而关联容器则根据关键字的值来存储元素。
1.概述
string和vector将元素保存在连续的内存空间,所以支持连续随机访问,在尾部插入数据快,在其他地方插入数据慢,因为要挪动很多。
list和forward_list不支持元素随机访问,访问一个元素只能遍历,但在任意位置添加删除都很快。
deque是双端队列,支持快速随机访问,在两端添加删除快。
array是固定大小数组,支持快速随机访问,不能添加或者删除元素。
**通常vector是最好的选择。**23333
2.容器库概览(所有容器都适用的操作)
每个容器定义在一个头文件内。
容器均被定义为模板类(模板分为类模板和函数模板)。
1.可以保存的类型
顺序容器几乎可以保存任意类型的元素,容器的元素可以是另一个容器。
vector<vector> lines;`
2.迭代器
迭代器有公共的接口。**如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都相同。**例如标准容器类型的迭代器都允许访问容器中的元素,而所有迭代器都是通过解引用运算符来实现这个操作,类似标准库迭代器都定义了递增运算符。
**迭代器范围:**一对迭代器,指向同一个容器中的元素或者是尾后元素,而且end不在begin之前,那begin和end构成一个迭代器范围。迭代器范围是左闭合区间,即[begin,end).
3.容器类型成员
比如size_type, iterator, const_iterator这些。
反向迭代器的++得到上一个元素。
3.begin和end成员
a.begin(); //返回第一个元素
a.rbegin(); //返回反向迭代器
a.cbegin(); //返回const迭代器
4.容器定义和初始化
每个容器类型都定义了一个默认构造函数,除了array之外,其它容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值的参数。
可以将一个容器初始化为另一个容器的拷贝;可以列表初始化;只有顺序容器的构造函数才接受初始化中的大小参数。
array初始化需要考虑大小,array<int 10>,以及内置数组类型虽然不能拷贝,但是array可以的哦(不愧是你)
5.赋值和swap
可以直接赋值,要求运算对象具有相同的类型。或者用assign(array除外),将一个容器的子序列赋值给另一个容器。
swap(a,b)交换a,b中的元素,对于array,交换时真正交换了元素,对于其它的只是交换了内部数据结构,也就是说iter在交换前指向a[3],交换后指向b[3]。
6.容器大小操作
成员函数size返回容器中元素数目(forward_list不支持),empty在size为0时返回true,max_size返回一个大于等于该类型容器能容纳最大元素数的值。
7.关系运算符
比较两个容器实际上是进行元素的逐个比较,容器的关系运算符使用元素的关系运算符完成比较。
每个容器类型都支持==和!=,除了无序关联容器其他的都支持>, <, >=, <=。
3.顺序容器操作
顺序容器和关联容器的不同之处在于二者组织元素的方式,这些不同之处直接关系到元素如何存储,访问,添加及删除。
1.向顺序容器添加元素
向vector, string, deque中插入元素会使得所有指向容器的迭代器,引用和指针都失效。因为会引起整个对象存储空间的重新分配,需要分配新的内存,并将元素从旧的空间移动到新的空间。
1.push_back(vector, string, deque, list)
将一个元素追加到尾部。
当我们使用对象初始化容器或者把对象插入容器时,实际上放的都是拷贝,随后容器中元素的任何改变都不会影响到原始对象。
2.push_front(deque, list, forward_list)
将元素插入头部。可以实现插入4 3 2 1这种逆序。
3.insert(vector, string, deque, list, forward_list的insert比较特殊)
将元素插入到所指定位置之前。insert成员函数接受一个迭代器作为其第一个参数。
a.insert(iter, "hello")
在vector, string, deque中可能会很耗时间的哦
第一个参数后时要插入的内容,可以是一个字面值,也可以是元素数目和值,或者一对迭代器指定的范围(但是不能是目标容器的迭代器)。
新标准下,接受元素个数或范围的insert返回指向第一个新加入元素的迭代器,旧标准为void。
list<string> 1st;
auto iter=1st.begin();
while(cin>>word)
iter=1st.insert(iter,word);//iter一直指向第一个元素,相当于push_front
4.emplace
empalce_front, emplace, emplace_back分别对应push_front, insert, push_back,但是是构造而不是拷贝元素,用的时候看书吧。。。
2.访问元素
front成员函数和back成员函数。
c.front()相当于*c.begin(), c.back()相当于对last迭代器递减后解引用。forward_list不支持back成员函数。
访问成员函数(front,back,下标和at)返回的是引用,如果使用auto来保存函数的返回值,记得定义为引用类型。
不能访问空容器。
auto &v=c.back();
提供快速随机访问的容器都提供下标访问,下标运算符接受一个下标参数,返回容器中该位置元素的引用。或者使用at成员函数。
3.删除元素
删除deque中除首尾元素之外的元素会使所有迭代器,引用和指针失效;指向vector或string中删除点之后位置的迭代器,引用和指针会失效。
删除的成员函数有pop_back(), pop_front(), erase,clear。
4.特殊的forward_list操作
当添加或者删除一个元素需要访问前面的元素的链接,但是这个使单向链表。
forward_list未定义insert, emplace,和erase,而是定义了insert_after, emplace_after和erase_after。
5.改变容器大小resize
resize成员函数。
6.容器操作可能使迭代器失效
添加元素:
vector或者string,存储空间被重新分配了则迭代器引用指针都失效,未重新分配指向插入位置之后的迭代器引用指针失效;
deque,插入首尾位置迭代器会失效,但是指向存在元素的指针引用不失效,插入其他位置都会失效;
list和forward_list仍有效。
删除元素:
vector和string,指向删除元素之后的迭代器引用指针失效;
deque,删除尾元素则尾后迭代器失效,其它不影响;
list和forward_list不影响。
添加/删除vector, string, deque元素的循环程序必须考虑失效问题,所以必须保证每个循环步中都更新迭代器,引用或者指针。如果循环调用的是insert或者erase,那么更新迭代器很容易,因为这些操作都返回迭代器。
不要保存end的返回值,每次操作之后重新调用end多好啊,速度也快。
4.vector对象是如何增长的
vector将元素连续存储,添加元素每次都要重新分配内存空间。所以可以预留一些空间,虽然每次要移动所有元素,但是比重新分配内存高效。
1.管理容量的成员函数
c.capacity(); //不重新分配内存的话,c可以保存多少元素
c.reserve(); //分配至少能容纳n个元素的内存空间,不改变容器中元素的数量
2.capacity和size
size是指已经保存的元素数目,capacity是指在不分配内存空间的前提下最多可以保存多少元素
我笑死了shrink_to_fit只是一个请求,标准库不保证退还内存,那要他干嘛???
5.额外的string操作
标准库string类型定义了大量函数。
1.构造string的其它方法
2.substr操作
s.substr(pos,n); //返回一个string,包含s中从pos开始的n个字符的拷贝。
3.改变string的其它办法(append,replace)
insert和eraase除了可以接受迭代器指明位置,也可以用下标。
s.insert(s.size(), 5, ‘!’); //在s末尾插入5个感叹号
插入的可以是字符也可以是string。
append成员函数在string末尾直接插入。
s.append("hjkk");
replace成员函数相当于先erase再insert。
s.replace(11,3,"ghui"); //在s的11位置处删除后面的三个元素在加上ghui
p324有具体重载的操作。
4.string搜索操作find
string提供了6和搜索函数,每个有四个重载。
搜索返回string::size_type值,表示匹配发生位置的下标,是一个unsigned类型,因此不用int或者其它有符号的类型来保存,可以用auto;
**搜索失败返回名为string::npos的static成员,**标准库将npos定义为一个const string::size_type类型,并初始化为-1。
最简单的函数为find,大小写敏感。
查找第一次/最后一次出现的位置。查找任何一个字符第一次/最后一次出现的位置。查找第一个/最后一个不在其中的字符。
可以指定从哪里搜索,可以逆向搜索(rfind)。
5.compare函数
类似于c中的strcmp。具体看书吧
6.数值转换
字符1和字符5在一起表示数值15
int i=42;
string s=to_string(i); //转换为字符形式
double d=stod(s); //转换为数字
要转换为数值的string中第一个非空白字符必须是数值中可能出现的字符(+,-,.,0123456789)
如果不能转换。函数抛出invalid argument异常,如果转换得到的数值无法用任何类型表示,抛出out_of_range的异常。
6.容器适配器
适配器是标准库中的一个概念,本质上是一种机制,让某种事物的行为看起来像另一种事物。
容器,迭代器和函数都有适配器,一个容器适配器接受一种已有的容器类型,让其行为看起来像一种不同的类型。
除了顺序容器外,标准库还定义了三个顺序容器适配器:stack, queue和ppriority_queue。stack接受一个顺序容器(除了array和forward_list),让其操作看起来像一个stack一样。
1.定义适配器
所有适配器都要求容器具有添加,删除及访问尾元素的能力,所以不能用array和forward_list。
stack要求push_back, pop_back,back,所以可以使用除以上两种的其它;
queue要求back, push_back, front, push_front,所以可以构造于list或者deque;
ppriority_queue除了front, push_back, pop_back外好需要随机访问能力,所以可以构造于vector或者deque。
每个容器适配器都基于底层容器类型的操作定义了自己的特殊操作,我们只可以调用适配器操作,而不能使用底层容器类型的操作,例如stack的不能调用push_back,只能调用push。
2.栈适配器
stack类型定义在stack头文件中,默认基于deque实现。
先进后出。
3.队列适配器
queue和ppriority_queue适配器定义在queue头文件中。
先进先出。
最后
以上就是无奈毛巾为你收集整理的c++ primer学习笔记 第九章第九章 顺序容器的全部内容,希望文章能够帮你解决c++ primer学习笔记 第九章第九章 顺序容器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复