9.1 顺序容易定义
1, 为了定义一个容器类型的对象,必须先包含相关的头文件,即下列头文件之一:
要定义某种特殊的容器,必须在容器名后加一对尖括号,尖括号里面提供容器中存放的元素的类型:
#include <vector> vector<string> svec; // empty vector that can hold strings
#include <list> list<int> ilist; // empty list that can hold ints
#include <deque> deque<Sales_item> items; // empty deque that holds Sales_items
2, 所有容器类型都定义了默认构造函数,用于创建指定类型的空容器对象。默认构造函数不带参数。除了默认构造函数,容器类型还提供其他的构造函数,使程序员可以指定元素初值。
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 个值初始化(第 3.3.1 节)(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)初始化为一段元素的副本 尽管不能直接将一种容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器(第 3.4 节)间接实现该实现该功能。使用迭代器时,不要求容器类型相同。容器内的元素类型也可以不相同,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制。
// 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[] = {"stately", "plump", "buck", "mulligan"};
// 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);
3) 分配和初始化指定数目的元素 创建顺序容器时,可显式指定容器大小和一个(可选的)元素初始化式。容器大小可以是常量或非常量表达式,元素初始化则必须是可用于初始化其元素类型的对象的值:
const list<int>::size_type list_size = 64;
list<string> slist(list_size, "eh?"); // 64 strings, each is eh?
创建容器时,除了指定元素个数,还可选择是否提供元素初始化式。我们也可以只指定容器大小:
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("Chimera"));
9.2 迭代器和迭代器范围
1, 迭代器常用的操作
*iter 返回迭代器 iter 所指向的元素的引用
iter->mem 对 iter 进行解引用,获取指定元素中名为 mem 的成员。等效于 (*iter).mem
++iter iter++ --iter iter-- 给 iter 加 1或者减1
iter1 == iter2 iter1 != iter2 比较两个迭代器是否相等(或不等)
2,vector 和 deque 容器的迭代器提供额外的运算
iter+n iter-n
iter1+=iter2 iter1-=iter2 这里迭代器加减法的复合赋值运算:将 iter1 加上或减去 iter2 的运算结果赋给 iter1
iter1 - iter2
>, >=, <, <=
3, C++ 语言使用一对迭代器标记迭代器范围(iterator range),这两个迭代器分别指向同一个容器中的两个元素或超出末端的下一位置,通常将它们命名为 first 和 last,或 beg 和 end,用于标记容器中的一段元素范围。此类元素范围称为左闭合区间(left-inclusive interval),其标准表示方式为:
// to be read as: includes first and each element up to but not including last
[ first, last )
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&
2, begin和end成员 begin 和 end 操作产生指向容器内第一个元素和最后一个元素的下一位置的迭代器,如表所示。这两个迭代器通常用于标记包含容器中所有元素的迭代器范围。
c.begin() 返回一个迭代器,它指向容器 c 的第一个元素
c.end() 返回一个迭代器,它指向容器 c 的最后一个元素的下一位置
c.rbegin() 返回一个逆序迭代器,它指向容器 c 的最后一个元素
c.rend() 返回一个逆序迭代器,它指向容器 c 的第一个元素前面的位置
3, 在顺序容器中添加元素 ,容器元素都是副本
所有顺序容器都支持 push_back 操作,提供在容器尾部插入一个元素的功能。
container.push_back(text_word); //在容器尾部添加新元素,并使容器长度加 1,新元素的值为 text_word 对象的副本.
除了 push_back 运算,list 和 deque 容器类型还提供了类似的操作:push_front。这个操作实现在容器首部插入新元素的功能。
使用 push_back 和 push_front 操作可以非常方便地在顺序容器的尾部或首部添加单个元素。而 insert 操作则提供了一组更通用的插入方法,实现在容器的任意指定位置插入新元素。
c.insert(p,t) 在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回指向新添加元素的迭代器
c.insert(p,n,t) 在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型
c.insert(p,b,e) 在迭代器 p 所指向的元素前面插入由迭代器 b 和 e 标记的范围内的元素。返回 void 类型
4, 容器大小的操作
c.size() 返回容器 c 中的元素个数。返回类型为 c::size_type
c.max_size() 返回容器 c 可容纳的最多元素个数,返回类型为 c::size_type
c.empty() 返回标记容器大小是否为 0 的布尔值
c.resize(n) 调整容器 c 的长度大小,使其能容纳 n 个元素,如果 n < c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素
c.resize(n,t) 调整容器 c 的长度大小,使其能容纳 n 个元素。所有新添加的元素值都为 t
5,访问元素
c.back() 返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定义
c.front() 返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义
list<int>::reference val2 = ilist.front();
c[n] 返回下标为 n 的元素的引用。如果 n <0 或 n >= c.size(),则该操作未定义
c.at(n) 返回下标为 n 的元素的引用。如果下标越界,则该操作未定义。只适用于 vector 和 deque 容器
6,删除元素
c.erase(p) 删除迭代器 p 所指向的元素。返回一个迭代器,它指向被删除元素后面的元素。
c.erase(b,e) 删除迭代器 b 和 e 所标记的范围内所有的元素,返回一个迭代器,它指向被删除元素段后面的元素。
c.clear() 删除容器 c 内的所有元素。返回 void
c.pop_back() 删除容器 c 的最后一个元素。返回 void。如果 c 为空容器,则该函数未定义
c.pop_front() 删除容器 c 的第一个元素。返回 void。如果 c 为空容器,则该函数未定义。只适用于 list 或 deque 容器
7,赋值与 swap
c1 = c2 删除容器 c1 的所有元素,然后将 c2 的元素复制给 c1。c1 和 c2 的类型(包括容器类型和元素类型)必须相同
c1.swap(c2) 交换内容:调用完该函数后,c1 中存放的是 c2 原来的元素,c2 中存放的则是 c1 原来的元素。c1 和 c2 的类型必须相同。该函数的执行速度通常要比将 c2 复制到 c1 的操作快
c.assign(b,e) 重新设置 c 的元素:将迭代器 b 和 e 标记的范围内所有的元素复制到 c 中。b 和 e 必须不是指向 c 中元素的迭代器
c.assign(n,t) 将容器 c 重新设置为存储 n 个值为 t 的元素
9.5 容器选择
1, 如果程序要求随机访问元素,则应使用 vector 或 deque 容器。
2,如果程序必须在容器的中间位置插入或删除元素,则应采用 list 容器。
3,如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用 deque 容器。
4,如果只需在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑在输入时将元素读入到一个 list 容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的 list 容器复制到一个 vector 容器。
9.7 容器适配器
1, 了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue 和 stack。适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack(栈)适配器可使任何一种顺序容器以栈的方式工作。表列出了所有容器适配器通用的操作和类型:
size_type 一种类型,足以存储此适配器类型最大对象的长度
value_type 元素类型
container_type 基础容器的类型,适配器在此容器类型上实现
A a 创建一个新空适配器,命名为 a
A a(c) 创建一个名为 a 的新适配器,初始化为容器 c 的副本
关系操作符 所有适配器都支持全部关系操作符:==、 !=、 <、 <=、 >、 >=
2, 适配器的初始化
所有适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器参数的构造函数将参数容器的副本作为其基础值。例如,假设 deq 是 deque<int> 类型的容器,则可用 deq 初始化一个新的栈,如下所示:
stack<int> stk(deq); // copies elements from deq into stk
3,覆盖基础容器的类型
默认的 stack 和 queue 都基于 deque 容器实现,而 priority_queue 则在 vector 容器上实现。在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型实参,可覆盖其关联的基础容器类型:
// empty stack implemented on top of vector
stack< string, vector<string> > str_stk;
// str_stk2 is implemented on top of vector and holds a copy of svec
stack<string, vector<string> > str_stk2(svec);
对于给定的适配器,其关联的容器必须满足一定的约束条件。stack 适配器所关联的基础容器可以是任意一种顺序容器类型。因此,stack 栈可以建立在 vector、list 或者 deque 容器之上。而 queue 适配器要求其关联的基础容器必须提供 push_front 运算,因此只能建立在 list 容器上,而不能建立在 vector 容器上。priority_queue 适配器要求提供随机访问功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。
4,栈适配器
s.empty() 如果栈为空,则返回 true,否则返回 stack
s.size() 返回栈中元素的个数
s.pop() 删除栈顶元素的值,但不返回其值
s.top() 返回栈顶元素的值,但不删除该元素
s.push(item) 在栈顶压入新元素
5,队列和优先级队列
标准库队列使用了先进先出(FIFO)的存储和检索策略。进入队列的对象被放置在尾部,下一个被取出的元素则取自队列的首部。标准库提供了两种风格的队列:FIFO 队列(FIFO queue,简称 queue),以及优先级队列(priority queue)。priority_queue 允许用户为队列中存储的元素设置优先级。这种队列不是直接将新元素放置在队列尾部,而是放在比它优先级低的元素前面。标准库默认使用元素类型的 < 操作符来确定它们之间的优先级关系。
q.empty() 如果队列为空,则返回 true,否则返回 false
q.size() 返回队列中元素的个数
q.pop() 删除队首元素,但不返回其值
q.front() 返回队首元素的值,但不删除该元素,只适合队列
q.back() 返回队尾元素的值,但不删除该元素,只适合队列
q.top() 返回具有最高优先级的元素值,但不删除该元素,只适合队列
q.push(item) 对于 queue,在队尾压入一个新元素,对于 priority_quue,在基于优先级的适当位置插入新元素