您的位置:首页 > 编程语言 > C语言/C++

C++ Container 以及 STL 相关的常用操作 和 注意事项

2011-11-20 22:08 489 查看
1. 对于list和各种associative containers(set, map, sultiset, multimap),erase()操作不会使指向被删元素以外的元素的iterator或reference失效,所以可以用下面的方法来删:

for (itr = asso_container.begin(); itr != asso_container
.end(); ) {
if (ShouldErase(*itr)) {
asso_container.erase(itr++);
} else {
++itr;
}
}
   对于vector或者deque,erase()操作还会影响指向其他元素的iterator和reference,所以不能这么删。比较有文化的方法是用STL的remove_if()和对区间的erase()操作配合来做这个事:
container.erase(remove_if(container.begin(), container.end(), ShouldErase), container.end());

如果ShouldErase(...)函数比较复杂,可能还要借助boost::bind()来构造unary function。如果情况再复杂一些的话(比如要把删掉的元素输出),那这个方法可能就不好使了。另外,使用remove_if(...)时ShouldErase肯定不能内联展开了,所以如果效率敏感的话,也要注意。一种基于的方法是:

for (itr = seq_container.begin(); itr != seq_container.end(); ) {
if (ShouldErase(*itr)) {
DoOtherThings(*itr);
itr = seq_container.erase(itr);
} else {
++itr;
}
}


这个方法的一个严重问题在于:当seq_container是vector,deque等连续存储容器时,seq_container.erase(itr)的复杂度可能是线性的!

所以我认为当效率敏感时,应该自己用for来模仿remove_if,可以在尽量不损失效率的情况下获得足够的灵活性。

补充:对于list,上面两种for循环的删法都是可以使用的,但一般会用第二个(seq_container)那种,因为毕竟list是sequential container。另外list有自己的remove和remove_if成员函数,不使用std::remove或std::remove_if.


2. std::vector a; 我们知道a.size()和a.capacity()不一定相等,也就是a可能有一定的预留空间。如果想释放这些预留空间怎么办呢?C++03里是用“swap大法” ,vector<int>(a).swap(a);;但到了C++11有了shrink_to_fit了就直接a.shrink_to_fit();就行了。注意:swap大法和shrink_to_fit()都可能会导致reallocation——执行前后a[0]的地址可能已经变化了(g++4.5.2上的实验结果)。另外,这里有很重要的一点需要说明:C++标准保证调用vector的clear()操作是不会更改capacity的(不知道新的标准有没有进一步保证不会发生reallocation,不过就算没保证,难道真的会有编译器在不改变capacity的情况下去reallocation?),这是很有用的一个保证——实际中经常希望一次reserve()到容量需求上界以后,反复增删元素和清空不会触发reallocation。

Update: 在《Effective STL》第15条发现了一段话:可以通过判断if (v.size() < v.capacity())来确定下次push_back或者single element insertion会不会触发reallocation。但是注意在非末尾进行insertion的话,即使没有触发reallocation,也会使从插入位置直到末尾的所有元素的iterators/pointers/references失效。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息