C++ Primer 第九章 顺序容器
标准库定义了三种顺序容器类型:vector、list 和 deque(是双端队列“double-ended queue”的简写,发音为“deck”)。
它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价。
标准库还提供了三种容器适配器(adaptors)。
实际上,适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。顺序容器适配器包括 stack、queue 和 priority_queue类型
顺序容器 |
|
vector<T> |
支持快速随机访问 |
list<T> |
支持快速插入/删除 |
deque<T> |
双端队列 |
顺序容器适配器 |
|
stack |
后进先出(LIFO)堆栈 |
queue |
先进先出(FIFO)队列 |
priority_queue |
有优先级管理的队列 |
), resieze()(重新划分容器容量)等操作; vector不用担心越界当空间不够用的时候,系统会自动按照一定的比例(对capacity( )大小)进行扩充。在vector序列末尾添加(push_back( ))或者删除(pop_back(
))对象效率高,在中间进行插入或删除效率很低,主要是要进行元素的移动和内存的拷贝,原因就在于当内存不够用的时候要执行重新分配内存,拷贝对象到新存储区,销毁old对象,释放内存等操作,如果对象很多的话,这种操作代价是相当高的。为了减少这种代价,使用vector最理想的情况就是事先知道所要装入的对象数目,用成员函式 reserve( ) 预定下来;vector最大的优点莫过于是检索(用operator[ ])速度在这三个容器中是最快的。
deque : deque是一个double-ended。queue是由多个连续内存块构成,deque是list和vector的兼容,分为多个块,每一个块大小是512字节,块通过map块管理,map块里保存每个块得首地址。因此该容器也有索引操作operator[ ],效率没vector高。另外,deque比vector多了push_front( ) & pop_front( )操作。在两端进行此操作时与list的效率 差不多。
学习顺序容器最重要的是了解每种容器的结构原理进而在实际应用中选择最合适的容器。
三种容器都是范型类型,可以定义不同的类型容器。
容器元素类型必须满足以下两个约束:
- 元素类型必须支持赋值运算。
- 元素类型的对象必须可以复制。
顺序容器有大量的属性和操作,最常用的是迭代器,增加,删除元素。
常用迭代器运算
*iter |
返回迭代器 iter 所指向的元素的引用 |
iter->mem |
对 iter 进行解引用,获取指定元素中名为 mem 的成员。等效于 (*iter).mem |
++iter iter++ |
给 iter 加 1,使其指向容器里的下一个元素 |
--iter iter-- |
给 iter 减 1,使其指向容器里的前一个元素 |
iter1 == iter2 |
比较两个迭代器是否相等(或不等)。当两个迭代器指向同一个容器中的同一个元素,或者当它们都指向同一个容器的超出末端的下一位置时,两个迭代器相等 |
iter + n |
在迭代器上加(减)整数值 n,将产生指向容器中前面(后面)第 n 个元素的迭代器。新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一位置 |
iter1 += iter2 |
这里迭代器加减法的复合赋值运算:将 iter1 加上或减去 iter2 的运算结果赋给 iter1 |
iter1 - iter2 |
两个迭代器的减法,其运算结果加上右边的迭代器即得左边的迭代器。这两个迭代器必须指向同一个容器中的元素或超出容器末端的下一位置 只适用于 vector 和 deque 容器 |
>, >=, <, <= |
迭代器的关系操作符。当一个迭代器指向的元素在容器中位于另一个迭代器指向的元素之前,则前一个迭代器小于后一个迭代器。关系操作符的两个迭代器必须指向同一个容器中的元素或超出容器末端的下一位置 只适用于 vector 和 deque容器 |
最重要的迭代器begin() 和end() 。
顺序容器添加元素操作
c.push_back(t) |
在容器 c 的尾部添加值为 t 的元素。返回 void 类型 |
c.push_front(t) |
在容器 c 的前端添加值为 t 的元素。返回 void 类型 只适用于 list 和 deque容器 |
c.insert(p,t) |
在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回指向新添加元素的迭代器 |
c.insert(p,n,t) |
在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型 |
c.insert(p,b,e) |
在迭代器 p 所指向的元素前面插入由迭代器 b 和 e 标记的范围内的元素。返回 void 类型 |
顺序容器的大小操作
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 |
看下面例子:
list<int> ilist(10, 42); //
定义10个值为
42 的的list
ilist.resize(15); // 增加了5个 0值项
ilist.resize(25,
-1); // 增加 10 个-1值项
ilist.resize(5); //
删除了20个项
resize 操作可能会使迭代器失效。在 vector 或 deque 容器上做 resize 操作有可能会使其所有的迭代器都失效。
对于所有的容器类型,如果 resize 操作压缩了容器,则指向已删除的元素迭代器失效。
vector 预分配机制: 可以在元素不存在的情况下预分配一段空间,为以后的存储做准备。这段空间可以用reserve()调节。capacity()返回的值就是可以存放元素的个数。capacity() - size()就是下次重新进行空间分配前的预留元素个数。至于max_size() 指的是一个vector结构可供储存元素的个数的上线,通常是由于寻址空间决定的。
访问顺序容器内元素的操作
c.back() |
返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定义 |
c.front() |
返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义 |
c[n] |
返回下标为 n 的元素的引用 如果 n <0 或 n >= c.size(),则该操作未定义 只适用于 vector 和 deque容器 |
c.at(n) |
返回下标为 n 的元素的引用。如果下标越界,则该操作未定义 只适用于 vector 和 deque容器 |
使用下标运算的另一个可选方案是 at 成员函数。这个函数的行为和下标运算相似,但是如果给出的下标无效,at 函数将会抛出 out_of_range 异常:
vector<string> svec; // 空容器
cout << svec[0]; //
run-time error: There are no elements in svec!
cout <<
svec.at(0); // throws
out_of_range exception
顺序容器有大量的成员可用,学习容器不需要记住所有成员操作时可查阅相关文档。
顺序容器还有三种适配器,主要作用是包装其它容器使之具有某种操作特征。
1 stack 堆栈适配器 ( 可用的容器类型 vector deque list)
2 queue 队列适配器 ( 可用的容器类型 deque list)
3 priority_queue 优先级队列 (可用的容器类型 deque vector)
#include <iostream>
#include <stack> //堆栈适配器头文件
#include <list>
using namespace std;
int main(void)
{
std::stack< int,std::list<int> > charStack;
cout << "入栈:" << endl;
for(int i=0;i<10;i++)
{
cout<<i+66<<endl;
charStack.push(66+i);
}
cout<<"出栈:"<<endl;
int size=charStack.size();
for (i=0;i<size;i++)
{
cout<<charStack.top()<<endl;
charStack.pop();
}
cout<<endl;
return 0;
}
queue用法:
#include <iostream>
#include <queue>
#include <list>
using namespace std;
int main()
{
std::queue< int,list<int> > intQueue;
cout << "入队:" << endl;
for(int i=1;i<=10;i++)
{
intQueue.push(i*100);
cout<<i*100<<endl;
}
cout<<"出队:"<<endl;
int size=intQueue.size();
for(i=0;i<size;i++)
{
cout<<intQueue.front()<<endl;
intQueue.pop();
}
return 0;
}
priority_queue 用法:
#include <iostream>
#include <queue>
#include <list>
using namespace std;
int main()
{
std::priority_queue< int,vector<int>,std::greater<int> > intPQueue; //优先级greater<type> 可换成 less<type>
intPQueue.push(100);
intPQueue.push(500);
intPQueue.push(600);
intPQueue.push(200);
intPQueue.push(300);
intPQueue.push(400);
int size=intPQueue.size();
for(int i=0;i<size;i++)
{
cout<<intPQueue.top()<<endl;
intPQueue.pop();
}
return 0;
}