1.顺序容器概述
一个容器就是一些特定类型对象的集合。顺序容器为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。
vector | 可变大小数组。支持快速随机访问,在尾部之外的位置插入或删除元素可能很慢 |
---|---|
deque | 双端队列。支持快速随机访问,头尾位置插入删除速度块,不支持在中间插入 |
list | 双向链表。只支持双向顺序访问,在list中任何位置进行插入、删除操作速度都很快,需要额外的空间 |
forward_list | 单向链表。只支持单向顺序访问,在任何位置进行插入、删除操作速度都很快,也需要额外空间 |
array | 固定大小数组。支持快速随机访问。不能添加或删除元素 |
string | 与vector相似,在尾部插入删除速度快 |
通常使用vector容器是最好的选择
新版的容器比旧版的容器快很多
大家可以思考一下:list和forward_list 和 vector和deque 容器的使用场景
容器库概览:
一般来说,每个容器都定义在一个头文件中,文件名与类型名相同。
顺序容器几乎可以保存任意类型的元素。
顺序容器构造函数的一个版本接受容器大小的参数,他使用了元素类型的默认构造函数。但某些类没有默认构造函数。我们可以定义一个保存这种类型对象的容器,但我们在构造这种容器时不能只传递给它一个元素数目参数:
vector<noDefault> v1(10,init); //正确示范,init 提供了元素初始化器
2.迭代器
所有容器的一个接口
begin和end 指向迭代器的首和尾
first和last 是指向第一个元素和最后一个元素的下一个位置
-
如果begin和end相等,则范围为空
-
如果begin和end不等,则范围至少包含一个元素,且begin指向该范围中的第一个元素
-
我们可以对begin递增若干次,使得begin == end
vector<int> ::value_type x;
value_type 就是将x返回一个容器的类型,我们就无须考虑容器内容到底是什么类型了。
reference 引用 绑定元素的引用
反向迭代器
reverse_iterator
迭代器版本有:iterator、reverse_iterator、const_iterator、const_reverse_iterator
当不需要写访问时,应该使用cbegin和cend
容器定义和初始化
每个容器都定义了一个默认构造函数。除array之外,其他容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值
array和内置数组的区别
与内置数组一样,标准库array的大小也是类型的一部分。当定义一个array时,除了指定元素类型,还要指定容器大小:
例如:array<int,42> array<string,10>
为了使用array类型,我们必须同时指定元素类型和大小:
array<int,10>::size_type i; //数组类型包括元素类型和大小
array<int>::size_type j; array<int> 不是一个类型
array的优势?
array可以进行赋值,但是内置数组不可以
3.赋值和swap
将一个容器初始化为另一个容器的拷贝
-
可以直接拷贝整个容器
-
拷贝一个迭代器对指定的元素范围
对于容器的直接拷贝,先看容器是否匹配,再看容器类型是否匹配,如果不匹配,就可以用元素范围来赋值
swap函数
swap只是交换了两个容器的内部数据结构,array外,swap不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。
元素不会被移动的事实,意味着,除string以外,指向容器的迭代器、引用和指针在swap操作之后都不会失效。他们仍指向swap操作之前所指向的那些元素。但是,在swap之后这些元素就属于不同的容器了。
容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。
assign函数
赋值运算符要求左边和右边的运算对象具有相同的类型。它将右边运算对象中所有元素拷贝到左边运算对象中。assign允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。
list<string> names; vector<const char*> oldstyle;names = oldstyle; names.assign(oldstyle.cbegin(),oldstyle.cend());
4.容器大小与关系
优先级的结合性!
5.顺序容器的操作
push_back; 压入一个值
push_front;在头部压入
insert;插入值
emplace;创建一个值
svec.insert(svec.begin(),"hello");
使用insert插入函数 插入头部
forward_list 有自己的insert和emplace版本;
forward_list 没有back函数 也不能递减forward迭代器
向一个vector、string或deque插入元素会使所有指向容器的迭代器、引用和指针失效。
容器元素是拷贝
访问元素
如果容器中没有元素,访问操作的结果是未定义的。
为了获取尾元素,必须首先递减此迭代器。另一个重要之处是,在调用front和back之前,要确保容器非空,否则可能会报错
at和下标操作只适用于string、vector、deque、array
在容器中访问元素的成员函数(front、back、下标和at)返回的都是引用,也就是说我们可以通过返回值来修改里面的值,如果不想被修改那么需要返回一个const值
当我们使用下标时,一定要注意合法性,不要超出范围
删除元素
pop_front和pop_back成员函数分别删除首元素和尾元素。与vector和string不支持push_front一样,这些类型也不支持pop。如果容器为空,那么也不能支持这些操作
erase函数
erase函数是用于删除容器中的元素的成员函数,比如std::vector、std::list等。erase函数通常返回指向删除元素之后的第一个元素的迭代器,或者返回一个迭代器指示容器末尾(如果删除的是最后一个元素)。具体返回值
erase可以从容器内部删除一个元素或一部分元素
list.erase(list.begin(),list.end());删除list中的全部元素
assign(ia,ia+11); 拷贝函数内容
特殊的forward_list操作
数据结构中的单向链表!
当添加或删除一个元素时,删除或添加的元素之前的那个元素的后继会发生改变。为了添加或删除一个元素,我们需要访问前驱,以便改变前驱的连接。
forward_list因此定义了insert_after/emplace_after和_erase_after的操作,为了支持这些操作,forward也定义了before_begin,它返回一个首前迭代器。第一个元素前面的指针
取决于容器类型和erase函数的重载版本。
注意各个函数的返回值,值得注意的是插入的返回值就是当前插入点,删除指向删除的下一个元素
例如:
6.改变容器大小
我们可以使用resize来增大或缩小容器,与往常一样,array不支持resize
list<int> ilist(10,42);
ilist.resize(15); 添加五个值为0的值
ilist.resize(25,-1);增加10个为-1的值
ilist.resize(5);删除后20个元素
如果元素类型是类类型,我们必须提供初始值,或者类必须有默认构造函数
改变容器大小的时候,考虑之前的指针是否失效!
当迭代器发生改变时,一定记得重新定义迭代器,强烈建议使用erase和insert函数,因为这两个函数有返回值,使用起来方便和安全。
7,vector对象是如何增长的
shrink_to_fit(); 将capacity减少为与size()相同大小
capacity(); 不重新分配内存空间的话,c可以保存多少元素
reserve(n);分配至少能容纳n个元素的内存空间
reserve并不改变容器中的元素的数量,它仅影响vector预先分配多大的内存空间。如果需求大小小于或者等于当前容量,reserve什么也不做。
capacity和size的区别
capacity是在不分配新的内存空间的前提下它最多可以保存多少元素,而size是指它已经保存的元素数量
每个vector实现都可以选择自己的内存分配策略。但是必须遵守的一条原则是:只有当迫不得己时才可以分配新的内存空间。
8.额外的string构造方法
类似于范围复制
substr操作
返回一个string,他是原始string的一部分或全部的拷贝。可以传递给substr一个可选的开始位置和计数值。
改变string的一些方法,类似前面提及的改变其他容器的函数,例如assign、insert、erase
s.insert(s.size(),5,'!'); //在s末尾插入5个感叹号
s.erease(s.size()-5,5); //从s
删除最后五个字符
s.assign(cp,7); 用cp中的内容替换s的内容
append 追加函数
s2.append(" hello");
replace替换函数
s2.replace(11,3,"5th"); 从11的位置开始,删除三个字符,替换成5th
string类的搜索
string类提供了6个不同的搜索函数,每个函数都有4个重载版本。每个搜索都返回一个string::size_type的值,表示匹配发生位置的下标,如果搜索失败返回一个名为string::npos的static的成员。
find函数完成最简单的搜索,查找参数指定字符串,若找到,则返回第一个匹配位置的下标,否则返回npos,区分大小写
string name("AnnaBelle");
auto pos1 = name.find("Anna"); // pos1 == 0;
find_first_of与find_first_not_of函数
string numbers("0123456789"),name("r2d2");
auto pos = name.find_first_of(numbsers); 返回1,即name中的第一个数字的下标
string dept("03714p3");
auto pos = dept.find_first_not_of(numbers); 返回5,字符‘p'的下标
find_first_of函数在实际应用中的使用很常用
compare函数
除了关系运算符外,标准库string类型还提供了一组compare函数,这些函数与C标准库的strcmp函数很相似。类似strcmp,根据s是等于、大于、小于参数指定的字符串,s.compare返回0、正数或负数。
数值转换
string可以与其他类型相互转换 接触的不多
9.容器适配器
stack和queue,关于容器适配器,就是该容器是基于其他类型的容器生成的,例如stack和queue适配器是根据deque容器生成的
使用容器适配器和使用普通的容器用法差不多,只是函数稍有区别
stack适配器
stack类型定义在stack头文件中,pop删除栈顶元素,不返回该元素值。
push压入一个元素到栈顶
emplace(args) 由args构造
s.top() 返回栈顶元素,但不删除元素
queue适配器
queue和priority_queue适配器定义在queue头文件中。
同样的,pop是返回首元素值,删除该元素
front返回首元素或尾元素
back只适用于queue
top返回最高优先元素,但不删除
push压入一个元素到末尾,而priority_queue中恰当的位置创建一个元素
q.emplace 创建一个元素