条款二十二
切勿直接修改set或multimap的键
而map和multimap则永远别修改作为键的对象
set和multiset可使用const_cast结合迭代器来修改不用做排序的键对象的其他属性。
但最好的做法是拷贝一份,对拷贝进行修改,移除旧值,加入改变的值。
条款二十三
考虑用排序的vector代替关联容器
关联容器提供对数级的查找时间,且每个元素带来三个指针的负载,适用于无法确定要执行什么操作的情况,即有插入又有删除又有查找等。
排序的vector适用于插入,查找,删除分阶段进行的情况,因为查找时vector需要更少的换页,击中率要更高。
使用vector代替关联容器的时候不能把key设置成const类型,因为vector的操作要基于复制/移动。而且还需要额外的查找/排序用到的比较子分别接受:
(pair,pair)进行排序
(pair,key)进行查找
(key,pair)进行查找
因为并不知道使用哪种形式比较。
条款二十四
当效率至关重要时慎选map:[ ]与map::insert()
[ ]在插入一个 不存在的值时相当于
re = insert(pos, value());
re->second = new_val;
此时应该使用insert来避免re这个临时对象的创建。
当加入的元素已经存在时,insert需要:
insert(pair()).first->second = v;
即insert需要构建一个pair然后通过返回的pair中的指针来修改
而operator[ 则不必,故应当选用[ ]。
条款二十五
哈希容器(略,介于和当前的无序容器有差异)
条款二十六
iterator优于其他iterator
注意,这一条不再适用,应该改成可以的话就使用const_iterator,见morden effective c++
尽管如此,这一条的其他东西还是值得一看。
const_iterator,reverse_iterator,const_reverse_iterator,iterator均是不同的类型,而不是是否加上const修饰符的区别
其中:
const无法转换为一般iterator
reverse通过.base()函数可转换成普通的iterator
普通的iterator可以转换成const或reverse
条款二十七
使用distance + advance转换const_iterator到iterator
是的,我刚说const版iterator无法转换到一般的iterator,但实际上还是有些特别的手段。
假如我们拥有一个const_iterator 名为 ci
auto be(stl.begin());//创建一个一般的iterator
advance(be, distance<const_iterator>(be,ci) );
其实制作了一个新的iterator让它和ci指向同个元素,此处的distance<const_iterator>
模板参数中的cosnt_iterator必须指定,因为distance接受的必须是两个类型相同的,至少具有inputiterator能力的迭代器。否则传入两个类型不同的迭代器会影响到distance的模板类型推断。
这种方法的消耗:
对于随机访问的迭代器->常数时间
其他为线性
由于现在各种成员函数也可以接受const版本的iterator所以尽量使用const_iterator(当然如果你要改变元素的话例外)
条款二十八
正确理解base()
base()会返回一个迭代器将reverse_iterator转换成iterator,但这个迭代器并不是指向reverse_iterator指向的元素,而是内存地址中下一个元素。
这一点是为了保证insert()操作会得到同样的效果,因为reverse_iterator是反向遍历的,所以插入一个元素的时候会插入到当前元素在内存地址上的后一位,故把reverse_iterator转换成正常的iterator时需要指向下一个元素来使得insert()得到一样的效果。
故等价的删除操作为erase((++reverse_iterator).base());
erase(--(reverse_iterator.base()));意思相同,但有的平台无法通过编译。
条款二十九
对逐个字符的输入考虑使用istreambuf_iterator
即使用流迭代器
istream_iterator使用operator>>来读取,即执行格式化读取
而
istreambuf_iterator则只是逐字符读取,无需设定相应的格式,检测不同的状态。
同理适用于ostreambuf_iterator与ostream_iterator
条款三十
保证目标区间足够大
如transform(val.begin(),val.end(),
result.begin(),
predicate);
对val遍历调用predicate(),返回true的返回值追加到result中,若result不够大则会出错,因为对result中无效对象赋值。
当然,可以把result.begin改成back_inserter(result)或back_inserter(result,result.begin()+n)来使用插入的方式,而不是改变原来的元素值。
条款三十一
排序
取出前二十个排序
partial_sort(stl.begin(), stl.begin()+20, stl.end(),less);//实际功能应该叫做把stl这个容器分为三段,begin()到begin()+20为有序
取出前二十个不排序(顺序不一定)
nth_element(stl.begin(), stl.begin()+20, stl.end(), less);
以上函数和sort均为不稳定,但是sort有稳定版本stable_sort,以上排序仅对随机访问容器有效
partition(begin(), end(), 条件);
符合条件放在容器前部,不符合放在后部,返回一个迭代器指向第一个不符合的元素,具有stable版本,可用于list
对于list等可以使用成员函数sort,它是稳定的。或者把元素复制到vector或迭代器放到vector。