6 STL容器
6.1 容器的共通操作
6.2 vector——<vector>
1、vector的容量(可由成员函数capacity()返回)很重要,如果超过就要重新配置内部存储器(很耗时),且和vector相关的所有reference、pointer、iterator都会失效。
std::vector<int> v; v.reserve(80); //申请容量,最好不要使用std::vector<int> v(80);因为后者要求元素提供默认构造函数,而前者无需初始化。
注意vector的容量不可缩减。一种方法为std::vector<T> tmp(v); v.swap(tmp);
2、几个需要注意的操作函数
vector<Elem> c(n, elem); //产生一个大小为n的vector,每个元素为elem,类似于c.assign(n,elem);
c.at(idx); //返回索引idx指向的元素。如果越界,抛出out_of_range异常。类似于c[idx],但后者不进行范围检查,越界会出现未定义行为。
c.front();与c.back(); //分别返回第一个/最后一个元素,若c为空,印发未定义行为。
安插和移除元素:迭代器必须指向合法位置,区间起始位置不能在结束位置之后,决不能从空容器移除元素。
c.insert(pos, elem); //在pos位置插入一个elem副本
c.insert(pos, n, elem); //在pos位置插入n个elem副本
c.insert(pos, beg, end);
c.resize(num); //将元素数量改为num(若size()变大,多出来的新元素以默认构造函数构造)
c.clear(); //清空所有元素
c.pop_back(); //注意无返回值,且c不能为空
4、异常处理
push_back()提供强保证, pop_back(),和swap()不抛出异常。
如果元素的拷贝(copy构造函数和assignment操作符)不抛出异常,所有操作提供强保证或不抛出异常。
6、class vector<bool>
内部一个bit存储一个元素,动态大小的位域。(如果需要静态大小的位域,应当使用bitset)
c.flip(); //所有bool元素取反
c[idx].flip(); //将索引idx的bit元素取反,注意其中使用了proxy的技巧
6.3 deque——<deque>
2、操作函数:
不提供容量操作(capacity()和reserve());
具有push_front() 和pop_front()
6.4 list——<list>
1、特性
不支持随机存取;安插和删除较快,且不会造成point、reference、itrerator失效;显然不需要提供容量、空间重新分配等操作函数。
2、操作函数:
增加c.remove(val) ; //移除所有值为val的元素
增加remove_if(op); //移除所有造成op(elem)结果为ture的元素
增加splice(铰接)函数:
注意:对于c.unique(op);任何一个元素elem,设其前一个元素为e,若op(elem, e);为true,则移除elem。注意op不能再函数调用过程中改变状态。
3 异常处理
几乎所有操作提供强保证。除了:
赋值运算和成员函数sort()只提供基本保证;
merge(), remove(), remove_if(), unique()要求元素间的比较(operator==或判断式predicate)不抛出异常,才能提供强保证。
6.5 set和multiset——<set>
1、特性:
通常以平衡二叉树实现;自动排序造成不能直接改变元素值,不提供直接存取元素的任何操作函数。
2、操作函数:
c(op); //以op为排序准则,产生一个空的set
特殊的搜索函数:
注意:equal_range()返回一个pair。
元素的安插和移除:
//set的insert接口
pair<iterator, bool> insert(const value_type& elem); //若该值已存在,则返回bool值为0(插入失败)
iterator insert(iterator pos_hint, const value_tyoe& elem);
//multiset的insert接口
iterator insert(const value_tyoe& elem);
iterator insert(iterator pos_hint, const value_tyoe& elem);
注意:set、map等不支持insert(pos, num, elem);和insert(pos, begin, end); 而vector、deque、list不支持insert(begin, end); string所有形式都支持。
注意:erase()无返回值,与vector不同,这主要是对于关联式容器,返回后继元素颇为耗时。
3、异常处理
多重元素安插操作不能提供强保证,但单一元素安插操作和多元素删除操作提供强保证。
如果元素排序准则的复制/赋值操作会抛出异常,swap()也会抛出异常。
6.6 map和multimap——<map>
1、要求:key/value可复制且可赋值,对排序准则而言,key可比较。
2、操作函数:
包含与set类似的所有函数。
//注意1:c.insert(elem);安插key/value时,有以下三种方法
std::map<std::string, float> coll;
//使用value_type,可避免隐式类型转换
coll.insert(std::map<std::string,float>::value_type("abc",2.2);
//使用pair
coll.insert(std::pair<std::string, float>("abc", 2.2)); //隐式转换
coll.insert(std::pair<const std::string, float>("abc", 2.2)); //显示转换
//使用make_pair()
coll.insert(std::make_pair("abc", 2.2));
注意2:find(elem)中elem只能是key,不能使value,且find(key)速度较快。
//注意3:移除指定元素value的正确方法(对于key,调用coll.erase(key)即可)
for(pos = coll.begin(); pos!=coll.end(); ){
if(pos->second == value){
coll.erase(pos++); //因为一旦erase(pos)之后,当前pos失效。且对于关联式容器,erase()不返回后继。
}else{
++pos;
}
}
3、将map视为关联式数组
关联式容器并不提供元素的直接存取,必须使用迭代器,但是map是个例外。
m[key] 返回一个reference,指向键值为key的元素。如果该元素尚未存在,就安插该元素。(注意新元素的value必须有默认构造函数)
例:对于coll["otto"] = 7.7;如果不存在这个key,则向容器中插入"otto"/float(),之后将7.7赋值给这个新元素。
这提供了修改key的方法(一般采用先插入newkey,再删除oldkey):
coll["newkey"] = coll["oldkey"]; coll.erase("oldkey"); //假设"oldkey"这个键值已存在于容器中。
6.8 实现reference语义
对容器中的元素采用引用计数型智能指针(如boost中的shared_ptr<>)。
6.9 各种容器的使用时机
元素被移除时,deque可以自动缩减内存(因为它使用多个区块)。
如果需要关联式数组,就采用map;如果需要字典结构,就采用multimap。
注意:关联式容器每安插一个新元素,都进行一次排序,速度不及序列式容器的做法:先安插所有元素,再使用sort进行一次完全排序。
6.10 细说容器中的类型和成员
1、容器中的类型
value_type:元素类型,对于map和multimap是pair<const key-type, value-type>
reference, const_reference, iterator, const_iterator, ......