顺序容器 | 名称 | 支持 | 性能 |
vector | 可变大小数组 | 快速随机访问 | 在尾部之外的位置插入或删除元素可能很慢 |
deque | 双端队列 | 快速随机访问 | 在头尾位置插入/删除速度很快 |
list | 双向链表 | 只支持双向顺序访问 | 任何位置插入/删除速度都很快 |
forward_list | 单向链表 | 只支持单向顺序访问 | 任何位置插入/删除都很快 |
array | 固定大小数组 | 支持快速随机访问 | 不能添加/删除元素 |
string | 与vector相似 | 专门用于保存字符串 | 随机访问快,在尾部插入/删除快 |
1.特点:
string和vector保存在连续的内存空间中,所以下标访问很快。但在中间插入/删除会很慢,因为要移动修改位置后面所有的元素。
list和forward_list是为了令容器任何位置添加/删除都很快,但这两个不提供随机访问,只能遍历整个容器。同时与vector,deque和array相比, 额外的内存开销很大,因为链表的元素需要存储指向下个位置的指针。
array是一种更安全、更容易使用赋值的数组类型。
通常vector是最好选择,在中间插入和删除操作过多时,可以考虑使用list或forward_list。
2.迭代器:
迭代器都有公共的接口。所有迭代器都通过解引用运算符(*)来实现访问元素的操作。(有一个例外是forward_list不支持--,因为是单向链表)
迭代器范围:由两个迭代器表示。 [begin, end) 左闭右开区间。
begin指向非空此范围的第一个元素,end指向最后一个元素之后的位置。空范围:begin == end3,
3.容器初始化
创建容器A使用容器B进行拷贝时:两个容器类型和元素类型必须匹配。
list<string> A = {"a", "b"}; // 使用列表初始化
deque<string> B(A); // 错误,容器类型不匹配
用传递迭代器来拷贝一个范围时,就不要求容器类型相同了。
deque<string> C(A.begin(), A.end()); // 正确
定义array: array<int, 32>; // 保存32个int数组
虽然不能对内置数组进行拷贝或对象赋值,但是array可以。
其他容器定义:vcector<int> vecA(12, 3); // 12个int元素,每个的初始值为3
4.赋值:
- c1 = c2 将c1中的元素替换为c2中元素的拷贝。c1和c2必须类型相同
- c = {a, b, c} 将c1中的元素替换为初始列表中的元素拷贝(array不适用)
- swap(c1, c2) 或 c1.swap(c2) 交换c1和c2中的元素。c1和c2必须类型相同。通常比拷贝元素快得多
- seq.assign(b, e) 将seq中的元素替换为迭代器b到e范围所表示的元素。(b和e不能指向seq中元素)
- seq.assign(il) 替换为初始化列表il中的元素
- seq.assign(n, t) 替换为n个值为t的元素
assign操作不适用与关联容器和array。由于是旧元素被替换,所以传给assign的迭代器不能指向调用assign的容器。
swap操作元素本身未交换,只交换两个容器的内部数据结构。除array外,不对任何元素进行拷贝、删除或插入操作。意味着,除string外,指向容器的迭代器、引用和指针,在swap后不会失效。
例如c1和c2进行swap后,iter迭代器指向c1的第二个元素,swap之后,c1变为c2,iter指向c2的第二个元素。
swap两个array会真正交换它们的元素,所需时间与元素个数成正比。指针,引用等绑定的元素不变,但是元素的值已经发生了改变。
5.关系运算符:
除了无需关联容器外的所有容器都支持关系运算符(>、>=、<、<=)。容器类型必须相同才能比较
比较两个容器其实是元素的逐对比较。元素必须也定义了关系运算符。
*****************************以上操作适用于几乎全部容器(顺序和关联容器),以下为顺序容器专属*****************************
顺序容器与关联容器的区别在于组织元素的方式。
6.顺序容器中添加数据:
array不支持插入,删除操作。forward_list不支持尾部插入(带back的)。vector和string不支持头部插入(带front的)。
- push_back:将一个元素追加到容器尾部。 用一个对象初始化容器或将一个对象插入的容器中,实际上放入容器中的是对象值的一个拷贝。容器中的元素与提供值的对象之间没有任何关联。
- push_front:将一个元素插入容器头部。
- insert:在特定位置插入。第一个参数为迭代器,在该迭代器前进行插入操作。之后的参数可以为元素,n个元素,初始化列表,两个迭代器表示范围。要拷贝的范围不能指向与目的位置相同的容器。返回值指向插入的新的元素的首元素的迭代器。
- emplace操作:包括emplce_back、emplace_front、emplace。对应前三种操作。push和insert是将元素拷贝到容器中。而emplace是将参数传递给元素的构造函数,在容器管理的内存中直接构造元素。该函数的形参必须与元素构造函数的形参相匹配。
向一个vector,string或deque插入元素,会使指向容器的迭代器,指针和引用失效。
deque在首尾之外添加数据都需要移动。
vector和string在插入点往后需要移动,而且还有可能对整个容器对象存储空间的重新分配,分配一个新的内存,将旧元素移入。
list和forward_list指向容器的迭代器,引用和指针仍有效。
7.访问数据:
- front():每个顺序容器都有,包括array。
- back():除array外的每个顺序容器。
- c[n]:下标访问,n越界则函数行为未定义。
- c.at(n):下标访问,n越界抛出out_of_range的异常。
对空的容器使用front或back,就像使用越界的下标一样。一种严重的程序设计错误。
8.删除元素:
- pop_back():从容器尾部删除一个元素。forward_list不支持。
- pop_front():从容器头部删除一个元素。vector和string不支持。
- erase(iter):删除迭代器的元素。被删除元素之后的迭代器。forward_list有特殊版本的erase。
- erase(iterB, iterE):删除迭代器范围的元素。返回最后一个被删除元素后的迭代器。若iterE本身为尾后迭代器,则返回尾后迭代器。
- clear():删除所有元素。
被删除的元素,迭代器,引用和指针都失效。
删除deque首尾外的元素,都会使所有迭代器,指针和引用失效。
vector和string删除点之后的位置,迭代器,引用和指针会失效。
list和forward_list指向容器其他位置的迭代器,引用和指针仍有效。
9.forward_list的特殊操作:
forward_list为单链表结构,就以添加和删除只能对某个元素的后继进行操作。
forward_list有一个首前迭代器,指向第一个元素。
- list.before_begin():返回首前迭代器。
- list.insert_after:相当于insert。但是是在第一形参(迭代器)的后面进行插入的。
- list.emplace_after:相当于empace,但是是在迭代器后创建。
- erase_after:删除迭代器之后的元素。
注意:不要保存尾后迭代器!!因为操作容器时,尾后迭代器很大可能失效。
10.改变容器大小:
resize(n)或resize(n, t):调整容器大小。缩小容器则被删除的元素的迭代器等失效,vector,string和deque进行resize可能导致迭代器等失效。
11.vector对象如何增长:
vector使用连续的内存存储的,如果增加元素导致内存不够用时,会重新分配一块更大的连续内存,并将旧元素全部挪到新内存。
vector通常会被分配一个比需求空间大些的内存。
管理容量的函数:
- c.capacity():不重新分配内存的话,可以保存多少元素。 适用于vector和string。
- c.reserve(n):分配能容纳n个元素的内存。 适用于vector和string。
- c.shrink_to_fit():将capacity()减少为与size()相等。 适用于vector,string和deque。
只要没有操作需求超出vector的容量,就不能重新分配内存。
确保用push_back添加元素操作具有较高的效率。
向一个size为0的vector中,调用n次push_back插入n个元素,所花时间不能超过n的常数倍。
12.容器适配器:
除顺序容器外,标准库还定义了三种顺序容器适配器:stack,queue和priority_queue。
例如stack接收一种顺序容器(除array和forward_list外),并使其操作看起来像stack。
deque<int> dep;
stack<int> s(dep);
所有适配器要求容器有添加,删除和访问元素的功能。
- 栈适配器(stack):要求push_back,pop_back和back。所以除array和forward_list都可以。先进后出。
- 队列适配器(queue):要求back,push_back,front和push_front。所以它可以构造于list和deque之上。 先进先出。定义在queue头文件中。
- 优先级队列适配器(priority_queue):新加的元素排在优先级比它低的元素之前。