顺序容器的元素按照其位置存储和访问。除了顺序容器之外,标准库还提供了几种关联容器,其元素按照键(key)排序。每组容器都提供一组不同的时间和功能的折中方案。顺序容器根据位置来存储和访问元素,元素的排列次序与元素值无关,而是由元素添加到容器的顺序决定。标准库定义了三种顺序容器:vector、list、dequeue。他们的差别在于元素访问的方式以及添加和删除元素相关操作的运行代价。标准库还提供了三种适配器。适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口来适应基础的容器类型。顺序容器适配器包括stack、queue、priority_queue.
9.1 顺序容器的定义
9.1.1 容器元素的初始化
9.1.2 容器内元素的类型约束
9.2 迭代器和迭代器范围
9.2.1 迭代器范围
- 他们指向同一个容器中的元素或者超出末端的下一位置。
- 如果两个迭代器不相等,则对first反复自增运算,必须能够到达last。
9.2.2 使迭代器失效的容器操作
9.3 顺序容器的操作
- 在容器中添加元素;
- 在容器中删除元素;
- 设置容器的大小;
- 获取容器内的第一个和最后一个元素。
9.3.1 容器定义的类型别名
size_type |
无符号整型,足以存储此容器类型的最大可能容器长度 |
iterator |
此容器类型的迭代器类型 |
const_iterator |
元素的只读迭代器类型 |
reverse_iterator |
按逆序寻址元素的迭代器 |
const_reverse_iterator |
元素的只读(不能写)逆序迭代器 |
difference_type |
足够存储两个迭代器差值的有符号整型,可为负数 |
value_type |
元素类型 |
reference |
元素的左值类型,是 value_type& 的同义词 |
const_reference |
元素的常量左值类型,等效于 const value_type& |
9.3.2 begin和end成员
9.3.3 在顺序容器中添加元素
c.push_back(t) | 在容器c尾部添加值为t的元素,返回void类型 |
c.push_front(t) | 在容器c的前端添加值为t的元素,返回void。只适用于list和dequeue |
c.insert(p, t) | 在迭代器p之前插入t元素,返回指向新添加元素的迭代器 |
c.insert(p, n, t) | 在迭代器p之前插入n个t,返回void |
c.insert(p, b, e) | 在迭代器p之前插入[b, e)范围的元素,返回void |
9.3.4 关系操作符
9.3.5 容器大小的操作
c.size() | 返回c中元素个数,类型c::size_type |
c.max_size() | 返回c中可容纳的最多元素个数,类型为c::size_type |
c.empty() | 返回标记容器大小是否为0的布尔值 |
c.resize(n) | 调整容器大小,容纳n个元素,元素多了就删除,少了就添加,值初始化新元素 |
c.resize(n,t) | 调整容器大小,容纳n个元素,新添加的元素初始化为t |
9.3.6 访问元素
c.back() | 返回容器c的最后一个元素的引用,c为空则操作未定义 |
c.front() | 返回容器c第一个元素的引用,为空则未定义 |
c[n] | 返回下表为n的元素的引用,只适用于vector和dequeue |
c.at(n) | 返回下标为n的元素的引用,只用于vector和dequeue |
9.3.7 删除元素
c.erase(p) | 删除迭代器p所指元素,返回迭代器,指向被删除元素后面的元素 |
c.erase(b, e) | 删除[b, e),返回指向被删除元素段后面的元素的迭代器 |
c.clear() | 删除所有元素,返回void |
c.pop_back() | 删除c的最后一个元素,返回void |
c.pop_front() | 删除第一个元素,返回void,只适用于list和dequeue |
9.3.8 赋值与swap
c1 = c2;
//等价于如下代码
c1.earse(c1.begin(), c1.end());
c1.insert(c1.begin(), c2.begin(), c2.end());
赋值和assign操作使所有迭代器都失效,但是swap操作不会使迭代器失效,完成swap以后尽管元素已经放在另一个容器中,但是迭代器仍然指向相同的元素。c1 = c2 | 删除c1的所有元素,将c2的元素复制给c1。c1和c2类型必须相同 |
c1.swap(c2) | 交换内容,c1和c2内容必须相同。执行速度比复制要快。 |
c.assign(b, e) | 重新设置c的元素为[b, e),b和e必须不是指向c中元素的迭代器,类型可以不完全一样。 |
c.assign(n, t) | 将容器c重新设置为n个值为t的元素。 |
9.4 vector容器的自增长
9.5 容器的选用
- 在容器的中间位置添加和删除元素的代价;
- 执行容器元素的随机访问的代价。
- list容器表示不连续的内存区域,允许向前和向后逐个遍历元素,在任何位置高效地insert和erase一个元素。list不支持随机访问。
- vector除了容器尾部,其他任何位置插入元素都要移动后面的元素。
- deque两端插入和删除元素非常快,中间插入和删除元素代价非常高。可以在首部和尾部高效地insert和erase,支持对所有元素的随机访问。
- 在deque的首部和尾部插入元素不会使迭代器失效,首部和尾部删除元素只会使指向删除元素的迭代器失效,在deque其他任何位置插入和删除操作都将使指向该容器的所有迭代器失效。
- vector和deque都支持高效的随机访问,list就慢很多。
- 通常来说,除非找到选择使用其他容器的更好的理由,否则选用vector是最佳选择。
- 如果要随机访问,则应该选择vector或者deque;
- 如果要在容器中间位置插入或删除元素,则应该选择list;
- 如果程序不是在中间位置插入或删除,而是在首部或尾部插入和删除,则应该选用deque;
- 如果只需要读取的时候再中间插入,然后需要随机访问,可以考虑先用list读取,然后排序,接着讲list复制到vector中随机访问。
- 如果既要随机访问又要在中间插入和删除元素,此时就要综合考虑,看哪种操作更多来决定选择何种容器。
- 如果无法确定选用何种容器,代码中尽量用vector和list都提供的操作,使用迭代器,避免随机访问,这样必要时可以方便地将程序从vector改成list。
9.6 再谈string类型
9.7 容器适配器
size_type | 一种类型,足以存储适配器类型最大对象的长度 |
value_type | 元素类型 |
container_type | 基础容器类型,适配器在此基础上实现 |
A a; | 创建一个新的空适配器a |
A a(c); | 创建一个新适配器初始化为容器c的副本。 |
关系操作符 | 所有适配器都支持全部关系操作符:==,!=,<,<=,>,>= |
stack<string, vector<string> > str_stk;
stack<string, vector<string> > str_stk2(svec);
3. 适配器的关系运算:两个相同类型的适配器可以做相等、不相等、大于、小玉、大于等于、小于等于的关系比较,只要基础元素支持等于和小于操作符即可!9.7.1 栈适配器
s.empty() | 如果栈空返回true,否则false |
s.size() | 返回栈中元素个数 |
s.pop() | 删除站定元素,不返回其值 |
s.top() | 返回栈顶元素值,不删除 |
s.push(item) | 在栈顶压入新元素 |
9.7.2 队列和优先级队列
q.empty() | 返回true或false |
q.size() | 返回队列中元素的个数 |
q.pop() | 删除队首元素,但不返回其值 |
q.front() | 返回队首元素的值,但不删除。只用于queue |
q.back() | 返回队尾元素的值,但不删除,只用于queue |
q.top() | 返回具有最高优先级的元素值,但不删除,只用于priority_queue |
q.push(item) | 对于queue,在队尾压入新元素; 对于priority_queue,在基于优先级的适当位置插入新元素 |