STL 速查手册: http://download.youkuaiyun.com/detail/whd0810/4817808
vector
内存结构如图 1:

1、调用 push_back 时要考虑扩容、内存移动的开销,例如:
std::vector<int> ivec;
for (int i = 0; i < 10; ++ i)
{
std::cout << "size: " << ivec.size() << ", capacity: " << ivec.capacity() << std::endl;
ivec.push_back(i);
}
初始时 vector 申请 capacity (0) 大小的内存块,当内存用完后再一次调用 push_back 则会进行一次扩容(1.5 倍或者 2 倍),然后把旧数据移动到新的内存中。capacity 的变化序列为: 1 2 4 8 16,内存总共移动了 4 次,移动的元素个数为 15 个,这是一个巨大的开销。
如果大概知道元素的个数,可以调用 reserve 预留空间避免内存移动开销,改动后如下:
std::vector<int> ivec;
ivec.reserve(16);
...
初始的 vector 调用 [] 操作符来插入元素会引起数组越界(此时 capacity 的大小为 0),即使调用 reserve 预留了足够的空间也不应使用,因为 [] 不会修改vector内部的 finish 指针这会引起各种问题,增加新元素建议使用: push_back。
2、调用 erase 时要注意迭代器的失效问题
erase 删除元素后把后面的元素往前移动,此时后面的迭代器都会指向不正确的值,为安全起见应该不再使用与此容器相关的迭代器。在 for 循环中删除元素可用 erase 的返回值指向下一个元素的迭代器的特点进行删除,如下:
for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); )
{
iter = ((val == *iter)? ivec.erase(iter): ++ iter);
}
也可以使用: erase-remove 批量删除元素,remove 算法把容器中要删除的值移动到末尾返回第一个要删除元素的迭代器,代码如下:
ivec.erase(std::remove(ivec.begin(), ivec.end(), val), ivec.end());
remove 操作前后的内存结构如图 2:

map (multimap)
1、使用 [] 操作符时要注意的事项, operator [] 的具体实现如下:
_Tp& operator[](const key_type& __k) {
iterator __i = lower_bound(__k);
// __i->first is greater than or equivalent to __k.
if (__i == end() || key_comp()(__k, (*__i).first))
__i = insert(__i, value_type(__k, _Tp()));
return (*__i).second;
}
大概意思是:不存在则插入新的元素 (__k, _Tp()) 并返回新插入元素值的引用,存在则返回旧值的引用。
这会引起以下三个问题:
a) 查找不存在的元素则会新增一个新的元素 (__k, _Tp()),频繁查找会增加 map 的容量
b) 新增元素先调用一次元素的默认构造函数,然后再调用一次赋值函数,多了一次默认构造函数的调用
c) 如果已经存在相应的 key 值,则新值覆盖旧值且不报错
2、调用 erase 时要注意迭代器的失效问题
map 使用红黑树来实现,以节点指针为操作单位,键值对包含在节点指针里,所以 插入、删除操作都不会影响到其它迭代器也不会引起内存的移动。但删除时本身会失效所以在 for 里循环删除时要注意,代码如下:
for (std::map<int, int>::iterator iter = imap.begin(); iter != imap.end(); )
{
if (val == iter->first)
{
imap.erase(iter ++);
}
else
{
++ iter;
}
}
hash_map
hash_map 使用链表数组 + hash 的方式来实现,内存结构如图 3(大小: 5,元素个数: 3):

注意事项跟 map 相同
vector、 map、 hash_map 比较
|
插入 |
查找 |
删除 |
vector |
O(1) 有可能会涉及内存移动 |
O(n) |
O(n) |
map |
O(logn) 不涉及内存移动 |
O(logn) |
O(logn) |
hash_map |
O(1) 有可能会涉及重组 |
O(1) |
O(1) |
三种容器的适用情况:
vector: 元素比较少,遍历居多,增删改查不频繁
map: 需要维护元素有序,元素较多,增删改查频繁
hash_map: 不需要维护元素有序,元素较多,增删改查频繁
迭代器
stl 里有5种迭代器,它们的继承关系如图 4:
图 4
Input Iterator: 只读迭代器,支持: 右*, ++
Output Iterator: 只写迭代器,支持: 左*, ++
Forward Iterator: 单向移动可读写迭代器,支持: *, ++
Bidirectional Iterator: 双向移动可读写迭代器,支持: *, ++, --
Random Access Iterator: 随机迭代器,支持: *, ++, --, + n, - n
对于非随机迭代器,要加减n,最好调用:advance函数
例如:
vector: Random Access Iterator
map: Bidirectional Iterator
hash_map: Forward Iterator
stl 算法都是以它所需要的最低程度的迭代器类型,比如 find,声明如下:
template <class _InputIter, class _Tp>
inline _InputIter find(_InputIter __first, _InputIter __last,
const _Tp& __val);
当然,用 Forward, Bidirectional, Random 迭代器来调用都可以。但对于 nth_element 则只能使用Random 迭代器,否则编译出错。
template <class _RandomAccessIter>
inline void nth_element(_RandomAccessIter __first, _RandomAccessIter __nth,
_RandomAccessIter __last);
函数对象
函数对象在 stl 算法库中广泛应用的一种轻量级的对象。定义:具有函数特质的对象,只需要重载 () 即可,例如:
template <class _Tp>
struct greater : public binary_function<_Tp,_Tp,bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const { return __x > __y; }
};
stl 使用的两种函数对象类型是:unary function (只带一个参数), binary function (带两个参数)。常用的函数对象为:
1、带有一个参数的比较函数对象,例如: find_if 的第三个参数
template <class _InputIter, class _Predicate>
inline _InputIter find_if(_InputIter __first, _InputIter __last,
_Predicate __pred);
struct Equal
{
Equal(int i) : __val(i) {}
bool operator () (int __x) const { return __x == __val; }
int __val;
};
find_if(ivec.begin(), ivec.end(), Equal(3));
2、带有两个参数的比较函数对象,例如: sort 的第三个参数
template <class _RandomAccessIter, class _Compare>
inline void sort(_RandomAccessIter __first, _RandomAccessIter __last,
_Compare __comp);
struct Less
{
bool operator () (int __x, int __y) const { return __x < __y; }
};
sort(ivec.begin(), ivec.end(), Less());