STL:循环删除容器中元素的方法和陷阱
算法大师Donald Knuth:不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil )。 STL中的容器主要是两种:序列式容器和关联式容器。下面讲到的都是我在开发中曾经遇到过的一些问题,有些例子我做了修改,我想初学STL的人基本都会遇到这些问题。序列式容器:vector list string等等关联式容
算法大师Donald Knuth:不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil )。
STL中的容器主要是两种:序列式容器和关联式容器。
下面讲到的都是我在开发中曾经遇到过的一些问题,有些例子我做了修改,我想初学STL的人基本都会遇到这些问题。
序列式容器:vector list string等等
关联式容器:set map等
1、容器的选择,到底要选什么容器应该根据你的需求和以后的扩展来选,可以参考effective stl上的某个条款,说的很清楚,我就不扯了。
2、怎样正确的删除序列式容器中的数据(循环删除),这里以vevtor为例,至少有两大种方法,STL中任何容器都可以使用迭代器进行元素的遍历,当需要在遍历中删除某些元素时,容器中元素的布局(位置或者排序)会随之改变,当前迭代器所指示的元素也会发生变化,这时继续递增或者递减迭代器进行后续元素的遍历时就要特别小心。容器的元素删除方法很大程度上依赖于操作系统上STL的实现版本,不同的系统和STL版本均有不同的定义,当然我们这里以STL的标准来说明。
自己迭代实现删除:
vector<int>::iterator itVec = vectInt.begin();
// 删除所有值为1022的元素
for ( ; itVec != vectInt.end(); )
{
// 删除
if (*itVec == 1022)
itVec = vectInt.erase(itVect);
else
++itVec;
}
上面是正确的实现,容易犯的错误是在erase后还继续++inVect,这是错误的,vector内部实现erase之后itVect实际上已经指向了下一个元素,你再自增就会跳过一个元素,会引发什么问题就很难说了,如果刚好跳过了最后一个元素,还有可能程序在这个地方崩溃。list也可以用这种方法,同时list还可以用下面的方法,只有一点小改动:
list<int>::iterator itList = listInt.begin();
// 删除所有值为1022的元素
for ( ; itList != listInt.end(); )
{
// 删除
if (*itList == 1022)
listInt.erase(itList++);
else
++itList ;
}
vector不能用上面那种方法是因为vector内部是一块连续的内存,而list则是链表。
使用stl算法库中的remove remove_if来进行删除:
调用STL中算法 remove或者remove_if的陷阱,并没有真正的删除(你可以此时调用size方法来验证这一点),然后应该使用erase删除残余数据,remove或者remove_if的实现原理是用后面 未被删除的元素 来覆盖前面 应该被删除的元素。返回值ForwardIterator指向经移除后的最后元素的下一位置。如vector{0,1,2,3,3,4},执行remove(),希望移除所有值为3的元素,结果为{0,1,2,4,3,4},返回值ForwardIterator指向第5个元素。即:
0 1 2 3 3 4 移除前
0 1 2 4 3 4 移除后
这个方法对于自定义类型要自己实现访函数(函数对象),这里就不举例了。
两类方法的对比,其实后面的方法效率要好,如果数据大,用后面的方法,因为针对vector的话,用后面的方法不会像自己用循环去删除的时候,频繁的拷贝移动内存。
3、关联式容器循环删除
for(iter = map.begin(); iter != map.end();)
{
if (shouldDelete(*iter))
map.erase(iter++);
else
++iter;
}
因为关联式容器的erase并不返回下一个元素的迭代器,因此不能用iter = map.erase(iter)这样的表达式(有些版本的STL实现又可以这样写,我建议还是使用 map.erase(iter++)保证移植性)。
关联容器同样也可以用remove等来删除元素。
4、谨慎对待操作STL容器的方法begin end size的优化,常见是将他们提取到循环的外部,如果你执行删除操作,可能导致错误,因为在进行删除操作的时候,容器的end,size很有可能随时变化。list容器的end一直不会变化,但是放在for里面,编译器也会优化。除非你能保证for循环中不会改变容器的大小。
附录:
map使用:
第一种:用insert函数插入pair数据
Map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, “student_one”));
my_Map.insert(make_pair("d",4));
第二种:用insert函数插入value_type数据
Map<int, string> mapStudent;
mapStudent.insert(map<int, string>::value_type (1, “student_one”));
第三种:用数组方式插入数据
Map<int, string> mapStudent;
mapStudent[1] = “student_one”;
mapStudent[2] = “student_two”
参考文献:
[end]
更多推荐
所有评论(0)