| 容器名 | 特点 |
|---|---|
| vector | 可变大小数组,支持快速随机访问,在尾部之外的位置插入或删除元素可能很慢 |
| deque | 双端队列,支持快速随机访问,在头尾位置插入/删除速度很快 |
| list | 双向链表,只支持双向顺序访问,在list中任何位置进行插入/删除操作速度都很快 |
| forward_list | 单向链表,只支持单向顺序访问,在链表任何位置进行插入/删除操作速度都很快 |
| array | 固定大小数组,支持快速随机访问,不能添加或删除元素 |
| string | 与vector相似的容器,专门用于保存字符,随机访问速度快,在尾部插入删除速度快 |
string和vector等将元素保存在连续的内存空间中,由元素的下标计算其地址时速度非常快,但在容器中间添加或删除元素非常耗时,因为要移动插入和删除位置之后的所有元素,来保持连续存储。
1)大多数情况下选用vector,除非有特殊需求
2)程序有很多小的元素,且空间额外开销很重要时不要使用list或forward_list
3)需要随机访问时,选用vector或deque
4)在容器中间插入或删除元素,使用list或forward_list
5)需要在头尾位置插入或删除元素,不会在中间插入或删除操作,使用deque
使用容器时都需要导入对应的头文件,例如list头文件在list中,vector头文件在vector中
例如:
list<double> list; //保持double的list
deque<double> deq;//保存double的deque
顺序容器几乎可以保存任意类型的元素,在尖括号内指定元素类型。

迭代器有公共的接口,如果一个迭代器提供某个操作,那所有提供相同操作的迭代器都对这个操作的实现方式时相同的。
vector<int> m_vec = {1,2,3,4,5,6,7,8,9};
auto begin = m_vec.begin();
//vector<int>::iterator beign = m_vec.begin(); //等价于auto begin的方式,使用了类型推导
auto end = m_vec.end();
while (begin != end)
{
cout << "current value:" << *begin<使用区间迭代器遍历打印vector中的元素。
例如:
//通过list<string>定义一个迭代器类型
list<string>::iterator iter;
//通过vector<int>定义一个difference_type类型
vector<int>::difference_type cout;
返回带r的版本返回反向迭代器,以c开头的版本则返回const迭代器
例如:
list<string> a = {"Milton","Shakespeare","Austen"};
auto it1 = a.begin();
auto it2 = a.rbegin();
auto it3 = a.cbegin();
auto it4 = a.crbegin();
注:当不需要写访问时,应使用cbegin和cend
vector<int> v1; //默认构造函数
vector<int> v2(v1);//以拷贝的方式使用v1的值给v2默认初始化
vector<int> v3 = v2;//和v2的方式一样
vector<int> v4{ 1,2,3,4,5 };
vector<int> v5 = { 1,2,3,4,5 };//v4和v5等价
vector<int> v6(v5.begin(), v5.end());
vector<int> v7(8);//默认8个元素,都为默认值
vector<int> v8(8, 1);//默认8个元素,默认值都为1
auto v8_begin = v8.begin();
auto v8_end = v8.end();
while (v8_begin != v8_end)
{
cout << "v8 current value:" << *v8_begin << endl;
v8_begin++;
}
注:在使用拷贝时,两个容器的类型及其元素类型必须匹配。
使用时,只有顺序容器的构造函数才接收大小参数,关联容器并不支持。
赋值运算符,将左边容器中的全部元素替换为右边容器中的元素的拷贝。
例如:
array<int,10> a1 = {0,1,2,3,4,5,6,7,8,9};
array<int,10> a2 = a1;//将a2的内容替换为a1中元素的拷贝
a2 = { 1,2,3 };//赋值后,a2的大小为3
使用时需要先导入array的头文件
a1 = a2 将a1中元素替换为a2中元素的拷贝,a1和a2必须具有相同的类型
a1 = {1,2,3,4,...} 将a1中元素替换为初始列表中元素的拷贝(array 不适用)
swap(a1,a2) 交换a1和a2中的元素,a1和a2必须具备相同的类型, swap通常比从a1向a2拷贝快得多
a1.swap(a2)
assign操作不适用于关联容器和array
seq.assign(b,e) 将seq中的元素替换为迭代器b和e所表示的范围中的元素。迭代器b和e不能指向seq中的元素
seq.assign(il) 将seq中的元素替换为初始化il中的元素
seq.assign(n,t) 将seq中的元素替换为n个值为t的元素
注:顺序容器array除外,assign允许从一个不同但相容得类型赋值,或者从容器得一个子序列赋值。assign操作用参数所制定得元素替换左边容器中所有元素。
例如:
list<string> list(1);
list.assign(10, "Hello");//十个元素都是hello
操作交换两个相同类型容器的内容,调用swap后,两个容器中的元素将会交换
vector<string> v1(10);
vector<string> v2(20);
swap(v1, v2);
注:除了array外,交换两个容器的操作保证很快,元素本身并未交换,交换了两个人容器的内部数据结构
除了array以外,所有标准容器都有灵活的内容管理,在运行是,可以动态添加或删除元素来改变容器大小。
| 操作 | 描述 |
|---|---|
| c.push_back(t);c.emplace_back(args) | 在c的尾部创建一个值为t或由args创建的元素。返回void |
| c.push_front(t);c.emplace_front(args) | 在c的头部创建一个值为t或由args创建的元素。返回void |
| c.insert(p,t) ;c.emplace(p,args) | 在迭代器p指向的元素之前创建一个值为t或由args创建的元素。返回指向新添加的元素的迭代器。 |
| c.insert(p,n,t) | 在迭代器p指向的元素之前插入n个值为t的元素,返回指向新添加的第一个元素的迭代器。若n为0,则返回p |
| c.insert(p,b,e) | 将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素。返回指向新添加的第一个元素的迭代器。若返回为空,则返回p |
| c.insert(p,il) | il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前,返回指向新添加的第一个元素的迭代器,若列表为空,则返回p。 |
注:
1)fordword_list有自己转悠版本的insert和emplace,不支持push_back和emplace_back
2)vector和string不支持push_front和emplace_front。
3)向一个vector,string或deque插入元素会使所有指向容器的迭代器,引用和指针失效。
向一个vector或string添加元素可能引起整个对象存储空间的重新分配,并将元素从旧的空间移动到新分配的内存空间中。
当用一个对象初始化容器时,或将一个对象插入到容器中时,实际上放入到容器中的是一个对象值得拷贝,而不是对象本身。
每个顺序容器都有一个front函数,除forward_list之外所有顺序容器都有一个back函数,分别返回首元素和尾元素。
在使用之前需要检查容器内是否有元素,如果没有元素,则访问操作得结果是未定义得。
例如:
vector<string> v1(10);
if (!v1.empty())
{
auto vl1 = v1.front();
auto vl2 = v1.back();//forward_list不支持
}
顺序容器中访问元素的操作
| 功能 | 描述 |
|---|---|
| v.back() | 返回v中尾元素的引用,若v为空,函数行为未定义 |
| v.front() | 返回v中首元素的引用,若v为空,函数行为未定义 |
| v[n] | 返回v中下标为n的元素的引用,n是一个无符号整数 |
| c.at(n) | 返回下标为n的元素的引用,如果下标越界,则抛出异常->out_of_rang |
at和下标操作只适合用于string,vector,deque和array
back不适用于forward_list;
非array容器也有多种删除元素的方式,操作完成后会改变array容器的大小
| 功能 | 描述 |
|---|---|
| v.pop_back() | 删除v中尾元素,函数返回void |
| v.pop_front() | 删除v中首元素 |
| v.erase§ | 删除迭代器p所指向的元素,返回一个指向呗删除元素之后的元素的迭代器,若p指向尾元素,则返回尾后迭代器。 |
| v.erase(b,e) | 删除迭代器b和e所指定范围内的元素,返回一个指向最后一个呗删元素之后的元素的迭代器 |
| v.clear() | 删除v中所有的元素,返回void |
注:
forward_list 有特殊的erase,不支持pop_back;vector和string 不支持pop_front。
在删除元素时,必须要做好检查,保证删除的元素是存在的。
它是一个单向的链表,执行添加和删除元素的操作是通过改变给定元素之后的元素来完成。
| 功能 | 描述 |
|---|---|
| lst.before_begin() | 返回指向链表首元素之前不存在的元素的迭代器。 |
| lst.cbefore_begin() | 返回一个const_iterator |
| lst.insert_after(p,t) | 在迭代器p之后的位置插入元素。t是一个对象,n是数量 |
| lst.insert_after(p,n,t) ; lst.insert_after(p,b,e);lst.insert_after(p,il) | b和e 是表示范围的一堆迭代器,il是一个花括号列表。返回一个指向最后一个插入元素的迭代器。 |
| emplace_after(p,args) | 使用args在p指定的位置之后创建一个元素,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义 |
| lst.erase_after(p);lst.erase_after(b,e) | 删除p指向的位置之后的元素,或删除b之后直到e之间的元素,返回一个指向被删除元素之后元素的迭代器。 |
例如:
forward_list<int> flst = {0,1,2,3,4,5,6};
auto prev = flst.before_begin();
auto curr = flst.begin();
while (curr != flst.end())
{
if (*curr % 2)
{
curr = flst.erase_after(prev);
}
else {
prev = curr;
++curr;
}
}
除了array不支持resize,可以使用resize来增大或缩小容器。如果当前大小大于所需要的大小,容器后部的元素会被删除;如果当前大小小于新大小,会将新元素添加到容器后部。
例如:
list<int> ilist(10, 42);
ilist.resize(15);//将5个为0的元素添加到ilist的末尾
ilist.resize(25, -1);//将10个值为-1的元素添加到ilist的末尾
ilist.resize(5);//从ilist末尾删除20个元素s
list.resize(n) 调整list的大小为n个元素,若n为了防止频繁的扩容,vector会在每次扩容时,分配需求更大的内存空间,预留这些空间作为备用,减少扩容带来的移动元素的性能问题。提升效率
c.shrink_to_fit() 请将capacity减少为于size相同大小
c.capacity() 不重新分配内存空间的话,c可以保存多少元素
c.reserve(n) 分配至少能容纳n个元素的内存空间
注:
1)shrink_to_fit只适用于vector,string和deque
2)capacity和reserve只适用于vector和string.
reserve并不改变容器中元素的数量,它仅影响vector预先分配多大的内存空间。
当需要的内存空间超过当前容量,reserve调用才会改变vector的容量。如果需求大小小于当前容量,至少分配于需求一样大的内存空间。