一,容器的通性
1,将元素插入容器进行了一次拷贝操作,如果想要实现类似于”引用”的情况,可以用指针.
2,容器中的元素是有序的,即用一个iter从begin遍历到end,多次运行得到的结果是相同的.
3,一般有如下API:
a) 构造函数通常有这三种:ContType c, ContType c1(c2),ContType c(beg,end)
b) 支持各种赋值(=)和比较运算符(==,>,<等),按字典顺序进行比较
c) 支持迭代器:begin(),end(),rbegin(),rend()
d) 支持移动构造函数c1.swap(c2)和swap(c1,c2)
e) 插入函数叫insert,删除叫erase,erase会调用被删除元素的析构函数
f) 有如下几个大小相关的函数:size(),empty(),max_size()
二,Vector
1,可以将vector看成一个数组(动态的),这意味着它的查找,插入/删除操作的复杂度都是O(n),但从尾部插入/删除的复杂度是O(1),而且有一个很重要的能力:随机存取
2,既然是连续存储, 就必然涉及到内存问题. Vector有几个操作内存的函数,如max_size,reserve,capacity等,但通常你用不到他们,这是因为:
a) Vector预先分配了很大的空间,通常情况下你用不完它们(事实是大数据量的情况通常不采用vector来存,O(n)的找速度很蛋疼)
b) 预先reserve出更大的空间可以节省重新配置内存的时间,但节省时间的效果并不是很明显,因为重新配置内存可能比预想的快得多. 更明显的好处是可以节省空间,因为重新分配内存时需要而被的空间消耗.
c) Reserve只能增大内存,不能减少,而且reserve是有可能失败的(内存不足). 减少可以用swap:
vector tmp;
v.swap(tmp)
tmp.clear()
这条规则的另一个效果是你无法弄出一个maxsize小于系统默认值的vector来=_=
3,vector的随机存储可以用下标,也可以用at()函数,区别是at()有可能报异常
4,一次插入多个元素比一个一个插入多次要省一些时间.当然还是尽量用push_back
5,vector的元素分布于连续空间中,因此可以把vector当一个数组来操作
6,vector<bool>是一个特殊的版本,可以大量节省内存,相当于一个可动态扩展的bitsets但不是很常用(至少在互联网)
7,vector的另一个重要特性是不能直接释放内存, 即使你把一个
三,Deque
Deque(音deck)与vector非常像,基本可以理解为一个可以从头部插入元素的vector. 这个能力看起来很美,但实际中需要的场合不多,事实上vector远比deque常用.
与vector比的优点:
1. 可以从头部存储,复杂度O(1)
2. 使用不止一块内存,因此可以有比vector更大的maxsize,而且可以直接释放内存(vector也可以通过swap释放)
缺点:
1. 存取元素时的效率要低一些
2. 插入一个元素会使所有的元素的引用失效(vector仅后面的元素失效)
3. 插入/删除中间元素比vector更慢(虽然都是O(n))
API区别:
1. 多一个push_front和pop_front
2. 没有capacity和reserve
四,List
list是用一个双向链表实现的,因此其特性与vector区别很大:
1,拥有O(1)的插入/删除能力
2,不具有随机访问能力
3,无需任何内存分配等操作
4,插入和删除操作不会使任何元素的指针,引用和迭代器失效
API函数:
1,remove和remove_if函数提供了移除确定值的功能,比通用算法更快
2,sort,排序不能使用通用算法里的sort,需要用其自己的成员函数sort
3,merge, 对有序列表进行merge,与sort一起,不错的功能
4,splice系列,将一条链表(的一部分)加到另一条链表中
5,reverse, 反序,速度很快
6,对比vector的push_back和list的push_back的速度
7,对比vector和list的空间消耗
五,list与vector性能的比较
速度上vector和list各有千秋,当插入或移动元素时, vector需要拷贝&移动元素数据, 而list无需移动元素本身,但需要处理一些额外的指针,而且可能需要为每个元素申请&分配内存. 因此当处理简单类型(如int)时,vector速度更快, 当处理复杂类型时(如class,struct) list更快, 但总得来说二者的速度都在一个数量级上.