Sequential Containers
顺序容器
From C++ Primer fourth edition
author wcdj 2010-3-12 Arbor Day
容器是容纳特定类型对象的集合。 vector 类型是一种顺序容器 ( sequential container ),它将单一类型 元素聚集起来成为容器,然后根据位置 来存储和访问这些元素。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。
标准库定义了三种顺序容器 类型: vector 、 list 和 deque ( double-ended queue 双端队列)。它们的差别 在于:访问元素的方式,以及添加或删除元素相关操作的运行代价。
标准库还提供了三种顺序容器适配器 ( adaptor ): stack 、 queue 和 priority_queue 。适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。
顺序容器类型 | |
顺序容器 | |
vector | 支持快速随机访问 |
list | 支持快速插入、删除 |
deque | 双端队列 |
顺序容器适配器 | |
stack | 后进先出 (LIFO) 栈 |
queue | 先进先出 (FIFO) 队列 |
priority_queue | 有优先级管理的队列 |
容器只定义了少量的操作,大多数额外操作由算法库提供。
容器类型的操作集合形成了以下层次结构:
l 一些操作适合于所有容器类型。
l 另外一些操作只适合于顺序或关联容器类型。
l 还有一些操作只适合于顺序或关联容器类型的一个子集。
u 顺序容器的定义
为了定义一个顺序容器类型的对象,必须先包含下列头文件之一:
#include <vector>
#include <list>
#include <deque>
所有的容器都是类模板 。要定义某种特殊的容器,必须在容器名后加一对尖括号 ,尖括号里面提供容器中存放的元素的类型 :
vector<string> svec; // empty vector that can hold string s
list<int> ilist; // empty list that can hold int s
deque<Sales_item> items; // empty deque that holds Sales_item s
所有的容器类型都定义了默认构造函数 ,用于创建制定类型的空容器对象 。
u (顺序)容器元素的初始化
除了默认构造函数,容器类型还提供其他的构造函数,使程序员可以指定元素初值。
容器构造函数 | |
C<T> c; | 创建一个名为 c 的空容器。 C 是容器类型名,如 vector 。 T 是元素类型,如 int 或 string 。 |
C c(c2); | 创建容器 c2 的副本 c 。 c 和 c2 必须 具有相同的容器类型,并存放相同类型的元素。适用于所有容器。 |
C c(b,e); | 创建 c ,其元素是迭代器 b 和 e 标示的范围内元素的副本。适用于所有容器。 |
C c(n,t); | 用 n 个值为 t 的元素创建容器 c ,其中值 t 必须是容器类型 C 的元素类型的值,或者是可转换为该类型的值。 只适用于顺序容器 |
C c(n); | 创建有 n 个值初始化( value-initialized )元素的容器 c 。 只适用于顺序容器 |
1) 将一个容器初始化为另一个容器的副本
当不使用默认构造函数,而是用其他构造函数初始化顺序容器时,必须 指出该容器有多少个元素 ,并提供这些元素的初值 。同时指定元素个数和初值的一个方法是将新创建的容器初始化为一个同类型的已存在容器的副本。
vector<int> ivec;
vector<int> ivec2(ivec); // ok : ivec is vector<int>
list<int> ilist(ivec); // error : ivec is not list<int>
vector<double> dvec(ivec); // error : ivec holds int not double
注意 :将一个容器复制给另一个容器时,类型必须匹配:容器类型 和元素类型 都必须相同。
2) 初始化为一段元素的副本
不能直接 将一种容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器 间接实现该功能。
说明: 使用迭代器时,不要求容器类型相同 。容器内的元素类型也可以不相同 ,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制。
迭代器标记了要复制的元素范围,这些元素用于初始化新容器的元素。迭代器标记出要复制的第一个元素和最后一个元素 。采用这种初始化形式可复制不能直接复制的容器。更重要的是,可以实现复制其他容器的一个子序列:
// initialize slist with copy of each element of svec
list<string> slist( svec.begin() , svec.end());
// find midpoint in the vector
vector<string>::iterator mid =svec.begin()+svec.size()/2;
// initialize front with first half of svec: The elements up to but not including *mid
deque<string> front(svec.begin() , mid);
// initialize back with second half of svec: The elements *mid through end of svec
deque<string> back(mid , svec.end());
注意: 指针就是迭代器 ,因此允许通过使用内置数组中的一对指针初始化容器:
char *words[]={“my”,”name”,”is”,”wcdj”};
// calculate how many elements in words
size_t words_size=sizeof(words)/sizeof(char *);
// use entire array to initialize words2
list<string> words2(words , words+words_size );
说明: 使用 sizeof 计算数组的长度。
3) 分配和初始化指定数目的元素 ( 只适用于顺序容器 )
创建顺序容器时,可显示指定容器大小 和一个 ( 可选的 ) 元素初始化式 。容器大小可以是常量或非常量表达式,元素初始化式则必须是可用于初始化其元素类型的对象的值:
const list<int>::size_type list_size =64;
list<string> slist(list_size , “wcdj”); // 64 strings, each is wcdj
说明: slist 含有 64 个元素,每个元素都被初始化为 ”wcdj” 字符串。
list<int> ilist(list_size); // 64 elements, each initialized to 0
// svec has as many elements as the return value from get_word_count
extern unsigned get_word_count(const string &file_name);
vector<string> svec(get_word_count(“wcdj”));
说明: 不提供元素初始化式时,标准库将为该容器实现值初始化。采用这种类型的初始化,元素类型必须是内置 或复合类型 ,或者是提供了默认构造函数的类类型 。如果元素类型没有默认构造函数,则必须显示指定其元素初始化式。
u 容器内元素的类型约束
C++ 语言中,大多数类型都可用作容器的元素类型。容器元素类型必须满足以下两个约束:
(1) 元素类型必须支持赋值 运算。
(2) 元素类型的对象必须可以复制 。
u 容器适配器
除了顺序容器,标准库还提供了三种顺序容器适配器: queue 、 priority_queue 和 stack 。
适配器 (adaptor) 是标准库中通用的概念,包括容器适配器 、迭代器适配器 和函数适配器 。
本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。
容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如, stack( 栈 ) 适配器可使任何一种顺序容器以栈的方式工作。下表列出了所有容器适配器通用的操作和类型。
Common Adaptor Operations and Types( 适配器通用的操作和类型 ) | |
size_type | 一种类型,足以存储此适配器类型最大对象的长度 |
value_type | Element type.( 元素类型 ) |
container_type | 基础容器的类型,适配器在此容器类型上实现 |
A a; | Create a new empty adaptor named a. |
A a(c); | Create a new adaptor named a with a copy of the container c. |
Relational Operators( 关系操作符 ) | 所有适配器都支持全部关系操作符: == 、 != 、 < 、 <= 、 > 、 >= |
To use an adaptor, we must include its associated header:
#include <stack> // stack adaptor
#include <queue> // both queue and priority_queue adaptors
适配器的初始化
Each adaptor defines two constructors : the default constructor that creates an empty object and a constructor that takes a container and makes a copy of that container as its underlying value . For example, assuming that deq is a deque<int> , we could use deq to initialize a new stack as follows:
stack<int> stk(deq); // copies elements from deq into stk