容器:特定类型对象的集合(模板)
通用操作:CP295(表9.2)
顺序容器:顺序与元素加入容器的位置有关
//可变大小数组
#include <vector>
不能在循环中向vector中添加对象
//与vector相似,专门用于保存字符
#include <string>
//c++11 数组类型(固定大小)
#include <array>
//c++11 单向链表,,没有size操作
#include <forward_list>
//双向链表,
#include <list>
//双端队列
#include <deque>
容量类型
size_type
- 也可用于下标索引,因为编译器不会检查下标是否越界,所以该类型为unsigned可以确保下标不会小于0
- 只能对已存在的下标运算符执行下标操作
访问:
支持快速随机访问:
- vector(保存在连续空间)
- string(保存在连续空间)
- array
- deque
支持单向顺序访问:
- forward_list
只支持双向顺序访问:
- list
快速插入删除:
- 任何位置:
- forward_list
- list
- 头尾:
- deque
尾部:
- string
- vector
(在尾部之外需要移动后面的元素,可能需要额外分配空间)
禁止:
- array
选用原则:
- 相比内置数据结构推荐使用容器
- 通常首选vector
按照程序对数据读写的需求:
应用中占主导地位的操作决定容器的选择(即当访问和插入删除的要求无法兼容)
注意当- 只有在读取输入时需要在容器中间位置插入元素
- 需要随机访问
此时
- 如果必须要在中间插入,可以先使用list,再将内容拷贝到vector
- 如果不是,可以在vector中插入后调用sort函数进行排序
4 无法确定时,选择使用迭代器访问vector和list
这里写代码片
独享操作:CP299(表9.3)
初始化
- 默认初始化为空容器(除array皆可用)
vector<int> vec;
//当元素类型没有默认构造函数时需要传递初始化器
vector<int> vec(init);
- 圆括号:构造(除array皆可用)
vector<int> vec(10);//10个元素
vector<int> vec(10,1);//10个为1的元素
vector<string> vec(10);//错误
- 花括号:
- 列表初始化
- 除array皆可用
- 只有在无法执行列表初始化的时候才会考虑其他的初始化方式
vector<int> vec{10};//一个为10的元素
vector<int> vec{10,1};//一个为10和为1的元素
vector<string> vec{ "hi" };
//无法使用列表初始化,尝试使用默认值来初始化
vector<string> vec{10};//10个元素
vector<string> vec{ 10,"hi" };//10个都是"hi"的元素
//当元素类型没有默认构造函数时必须指定初始值
拷贝容器的一部分进行初始化
//通过指定范围初始化(除array皆可用)
//类型必须可以相互转换
vector<int> vec(ivec.begin(), ivec.end());
拷贝容器进行初始化
//ivec和vec的类型(元素和容器)相同
vector<int> vec(ivec);
vector<int> vec=ivec;
//array的size必须相同
列表初始化
当内置类型的初始值存在丢失信息的风险,(例如类型转换时丢失精度),编译器将报错。CP39
//对于array来说数量不够时会进行值初始化
vector<int> vec{ 1,2,3,4,5 };
vector<int> vec={ 1,2,3,4,5 };
//多个初始值只能使用列表初始化
array<int,10> a;
赋值
- 如果容器大小不同,赋值后为右边容器的大小
- array的元素类型必须相同
- 导致左边的迭代器,引用和指针失效
c1=c2;
c={a,b,c};//除array皆可用
assign CP302
swap
- 类型必须相同
- 速度更快(除array外只交换容器内部的数据结构,不交换元素)
- 对于array会交换元素
- 除array和string不会使迭代器,引用和指针失效(但指向的元素变换了所属的容器)
swap(c1,c2)//尽量使用非成员版
c1.swap(c2)
vector<string> sv1(5);
vector<string> sv2(10);
auto iter=sv1.begin();
/*交换后vec1有10个元素,vec2有5个元素
交换的是整个容器内容*/
swap(sv1,sv2);
//交换后iter指向sv2[0]
assign
- 仅顺序容器使用
- 类型相容即可
seq.assign(b,e)
seq.assign(i1)
seq.assign(n,t)
大小
c,size()
c.max_size()//可保存的最大元素数目
c.empty()
关系运算符:
- 除无序关联容器皆可
- 容器和元素类型必须相同
- 进行元素的逐对比较
元素的类型必须定义相应的比较运算符
- 容器的size和元素都相同才等于
- size不同时,size较小时容器的值都等于则小于
- 第1次出现相异字符的对比结果
添加
- array不可用
- 添加的是对象的拷贝,而不是对象本身
- 可能导致系统需要重新分配足够的内存并移动
(对于vector和string的迭代器,指针和引用都会失效) - emplace操作构造(并非拷贝)元素
- 对于提供参数必须保证定义了对应的构造函数
//vector和string不支持(需要移动所有元素)
//在头部创建
c.push_front(t);
//先根据参数创建局部临时对象,再放入容器
c.emplace_front(t);
//在容器内部根据参数直接创建对象
//在尾部创建
//forward_list不支持
c.push_back(t);
c.emplace_back(t);
c.insert(iter, p1);
- 插入到迭代器之前的位置
- 返回指向新添加的第1个元素的迭代器
list<string> lst;
auto iter=lst.begin();
while(cin>>word)
iter=lst.insert(iter,word);
- 可以用其代替push_front,但是根据前面容器的性质可能会很耗时
- 同时支持插入范围内元素
//指定数量的元素
vec2.insert(iter2, n, t);//迭代器位置之前插入n个t元素
//迭代器范围
ivec3.insert(iter3, ivec2.begin(), ivec2.end());
//迭代器的范围不能是同一个容器
//列表
ivec4.insert(ivec.begin(), {1,1,1});
forward_list有专有版本的insert_after和emplace_after
副作用
- vector和string:
如果存储空间并未重新分配,只有插入位置之后的迭代器,指针和引用都会失效。 - deque:
插入到首尾位置之外会导致迭代器,指针和引用都会失效。
在首位置添加只会导致迭代器失效。
- vector和string:
访问:
- 返回引用
- 有时比begin()/end()更加方便
- 禁止使用失效的迭代器,指针和引用(添加/删除的副作用)
- 在循环中添加/删除会注意保持迭代器保持正确的更新
- 由于尾后迭代器经常失效,必须重新调用end
auto begin=v.begin();
while( begin != v.end() )
{…}
- 容器为空时函数未定义,使用前必须确保容器非空
c.front() //返回首元素的引用
forward_list不支持
c.back() //返回尾元素的引用
- 只适用于支持随机访问的容器
程序必须检查保证下标不会越界
c[n] //n为无符号整数,超出size时函数未定义
//建议使用
c.at(n)
//n为无符号整数,超出size时抛出out_of_range异常
删除
- 不适用于array
- 必须保证被删除的元素是存在的,否则函数未定义
- -禁止使用失效的迭代器,指针和引用
- 如果需要则在pop之前保存被抛出的元素
- 删除尾元素
c.pop_back(); //除forward_list外皆可
删除头元素
c.pop_front(); //除vector和string外皆可
- forward_list有专有版本的erase_after
返回被删除元素的下一个元素的迭代器
c.erase(iter)
//删除迭代器p指向的元素,
当e为尾后迭代器时,返回尾后迭代器
vec.erase(iter1,iter2)
//删除迭代器(b, e)范围内的元素
- 删除所有元素
c.clear()
- 副作用
- 尾后迭代器总会失效
- 指向被删除元素的迭代器,指针和引用都会失效。
- vector和string:
删除位置之后的迭代器,指针和引用都会失效。 - deque:
删除首尾位置之外的元素会导致迭代器,指针和引用都会失效。
改变容器大小
- array除外
- 当size大于n时,后面的元素被删除
c.resize(n)
- 当size小于n时,将新元素添加到后面
c.resize(n)//新元素被默认初始化
c.resize(n,t)//新元素都初始化为t
容器容量管理
- 只适用于vector和string
由于vector/string的元素都是连续存储,为了在添加操作时减少重新分配的次数,实现分配时会额外预留一些空间备用。
c.capacity() //现分配的内存空间可以容纳的元素数量
- 当n>c.capacity()时,该函数才会改变容量(≧n);反之函数不做任何改变。
- 注意与resize的区别
- 尽量不要分配新的空间
c.reserve(n) //通知容器需要容纳的最小元素数量
- 只适用于deque、vector和string
可以向系统请求退回多余的内存空间(不一定执行)
c.shrink_to_fit() //将capacity()的数量减少为size()
forward_list
由于添加/删除单向链表的元素会改变序列的链接,为了维护,必须访问其前驱以便改变链接。
- 返回指向首元素之前位置的迭代器(无法解引用)
对forward_list需要2个迭代器
lst.before_begin()
lst.before_cbegin()
- 必须保证迭代器不为尾后迭代器
//将t插入到p的后面
lst.insert_after(p,t)
lst.insert_after(p,n,t)
//范围不能在lst内,范围为空返回p
lst.insert_after(p,b,e)
lst.insert_after(p,{…})
//在指定位置后创建新元素,返回指向新元素的迭代器
emplace_after(p,t)
- 必须保证迭代器不为尾后/指向尾元素的迭代器
//删除p之后的元素
lst.erase_after(p)
//删除从b开始到(不包括)e的元素
lst.erase_after(b,e)