C++primer第九章顺序容器

一、顺序容器类型

a:迭代器类型

vector:支持快速随机访问

list:支持快速插入和删除

deque:支持快速随机访问,支持头和尾快速插入和删除

b:容器存储方式

1、vector

就是动态数组.它也是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后内存也不会释放.如果新值>当前大小时才会再分配内存.

它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符。

元素是结构或是类,那么移动的同时还会进行构造和析构操作,所以性能不高 (最好将结构或类的指针放入vector中,而不是结构或类本身,这样可以避免移动时的构造与析构)。

2、list

就是双向链表,元素也是在堆中存放,每个元素都是放在一块内存中,它的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。

但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。

list<指针>完全是性能最低的做法,这种情况下还是使用vector<指针>好,因为指针没有构造与析构,也不占用很大内存。

3、deque  

是一个双端队列(double-ended queue),也是在堆中保存内容的.它的保存形式如下:
[堆1]
...
[堆2]
...
[堆3]
每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品.

它支持[]操作符,也就是支持随即存取,有比较高的随机访问速度,和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。

vector和deque的区别主要在于组织内存上不一样,deque是按页或块来分配存储器的,每页包含固定数目的元素.相反vector分配一段连续的内存,vector只是在序列的尾段插入元素时才有效率,而deque的分页组织方式即使在容器的前端也可以提供常数时间的insert和erase操作,而且在体积增长方面也比vector更具有效率。

deque在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。

c:容器选择的一般规则

 要求随机访问时候选vector和deque,必须要求中间插入和删除时候选用list,只要求在首位插入或删除的选用deque,若只须在中间输入时插入和删除对象,可先定义为list,然后赋值给vector 以方便以后的访问。具体无法确定时候可选用一种较为安全的做法就是,定义的时候尽量使用迭代器,而不要使用下表,这样可以方便的以需要的时候将vector改为list,而使程序能顺利的运行。

二、迭代器

vector<int>::const_iterator iter=vec.begin();//const_iterator只能用于读取容器内的元素,不能改变其值

const vector<int>::iterator iter=vec.begin();//const迭代器必须初始化,且一经初始化就不能再改变,只能用它来改变该迭代器指向的数值,因此这个基本没什么用处

a:迭代器失效

对于vector,当向容器添加元素时,如果容器大小刚好等于容器内存,则会造成容器的重载,容器的所有迭代器失效。

对于list容器,添加元素时,不会造成迭代器失效,而删除元素时,该迭代器指向的数据发生移位,应该注意更新迭代器

对于deque容器,标准中没有指明,添加元素时,迭代器是否会失效,尽管deque容器内部是一个链表,大多数迭代器并不失效,但是不能保证迭代器一定不会失效。

 

三、容器适配器

a:容器适配器类型

stack:后进先出,支持三种容器

queue:先进先出,队列,支持list、deque,要求容器支持pop_front()

priority_queue:要求能随机访问,支持vector和queue

 默认的stack和queue都基于deque容器实现,而priority_queue则在vector容器上实现。在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型参数,可覆盖其关联的基础容器类型。例如:   stack<int, vector<int> > int_stack; 

q.push(item) 对于queue,在队尾压入一个新元素; 对于priority_queue,在基于优先级的适当位置插入新元素

b:为什么要有容器适配器

容器vector,list,deuqe是C++STL中三种基本容器实现,它们不可能互为实现同时又不损失效率, 就像颜色中的三原色红绿蓝可以混成其他多种颜色.
而stack和queue则都可以在这三种基本容器序列基础上高效实现, 所以没有定义为独立的容器,而只作为基本容器适配器.
所以容器适配器所提供的是原来容器的一个受限的界面, 特别是适配器不提供迭代器.
所有stack和queue是用deque基本容器作为实现方式的.
容器适配器模板要提供两个类型1是容器中元素的类型, 2是选择的实现方式

四、经典例子

#include "iostream"
#include <vector>
#include <deque>
using namespace std;
int main()
{

vector<int> vv(2);
 vv[0]=10;
 vector<int>::iterator itV=vv.begin();
 cout<<vv.capacity()<<endl;
 cout<<*itV<<endl;
 int *p=&*itV;
 vv.push_back(20);
 cout<<"vector :"<<*p<<endl;


 deque<int>dd(2);
 dd[0]=10;
 deque<int>::iterator itD=dd.begin();
 int *q=&*itD;
 dd.push_back(20);
 cout<<"deque :"<<*q<<endl;
 system("pause");
 return 0;

}

输出结果2 10 vector:-17891602 deque:10

分析原因:

对于vector由于我们首先定义vector容量为2,因此系统之分配给vector2个内存,后来在添加元素时,容器需要重新分配内存,因此原先的位置无效了。而queue之所以没变,是因为它内部是个链表,没必要重新分配queue[0]的地址,但是这也没有任何保证,只是碰巧而已。

"如果说deque的是链表形式的话,只要没有删除元素,那之前所有的地址都应该是有效的吧,"-------deque没有这种保证!你不能看它是怎么实现的,可能msvc里面是这么实现的gcc里是那么实现的,标准库里没规定deque"一定是"不变的.p=&*itV,itD都没问题,*迭代器返回一个对应类型的引用,取这个引用的&,没问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值