遍历删除说明
STL中的容器按存储方式分为两类:
- 一类是按以数组形式存储的容器(如:vector 、deque)
- 另一类是以不连续的节点形式存储的容器(如:list、set、map)
在使用erase方法来删除元素时,需要注意一些问题
如下的方法对于所有容器都是有问题的
1
2
3
4
5
| Container<T>::iterator it;
for (it = container.begin(); it != container.end(); ++it) {
if (N == X)
container.erase(it);
}
|
其中it已经变为野指针,对它的++操作会造成异常
list,set,map
方法一1
2
3
4
5
6
7
8
9
10
| std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{
if( WillDelete( *itList) )
{
itList = List.erase( itList);
}
else
itList++;
}
|
方法二1
2
3
4
5
6
7
8
9
10
| std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{
if( WillDelete( *itList) )
{
List.erase( itList++);
}
else
itList++;
}
|
下面是两个错误的方法
错误一1
2
3
4
5
6
7
8
9
| std::list< int> List;
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); itList++)
{
if( WillDelete( *itList) )
{
List.erase( itList);
}
}
|
错误二1
2
3
4
5
6
7
8
9
10
11
| std::list< int> List;
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{
if( WillDelete( *itList) )
{
itList = List.erase( ++itList);
}
else
itList++;
}
|
错误一出现了野指针,而错误二在删除前就进行了偏移
vector,dequeue
vector erase1
2
3
4
5
6
7
8
| iterator erase(iterator position)
{
if (position + 1 != end())
copy(position + 1, finish, position); // 后续元素往前移动
--finish;
destroy(finish); // 一个释放资源的全局函数
return position;
}
|
由上述代码可以看出vector删除一个元素后所有元素后面的向前移动,然后返回删除后
的当前iterator,从指针上看没有变化,但是其实是返回删除元素的下一个元素的iterator
所以对于此类容器可以如下操作
vector erase1
2
3
4
5
6
7
8
9
10
11
| std::vector< int> Vec;
std::vector< int>::iterator itVec;
for( itVec = Vec.begin(); itVec != Vec.end(); )
{
if( WillDelete( *itVec) )
{
itVec = Vec.erase( itVec);
}
else
itList++;
}
|
注意不可以用上述方法二进行操作,因为如果进行++操作,相当于指向删除元素后一个元素的后一个
总结
C++的STL通过iterator将container和algorithm分离,并通过functor提供高可定制性。iterator可以看作是一种契约,
algorithm对iterator进行操作,algorithm很难对container进行直接操作,这是因为algorithm对container所知甚少,
一段代码,若未利用操作对象所知全部信息,将难以达到性能之极,并伴随其它种种折中现象。
当然,这种“未知性”是必须的——algorithm对于真正的操作对象container不能做出太多假设,若假设过多,
何来一个algorithm可以作用若干不同container的妙举,STL强大威力也将受损不少
erase一般作为一个container的成员函数,是真正删除的元素,是物理上的删除
作为算法部分的remove类函数,是逻辑上的删除,将被删除的元素移动到容器末尾,然后返回新的末尾,此时容器的size不变化
部分容器提供remove类成员函数,那么代表的是真正物理意义上的删除元素
如果该容器是vector、string或者deque,使用erase-remove idiom或者erase-remove_if idiom
如果该容器是list,使用list::remove或者list:remove_if成员函数
如果该容器是一个associative container,使用asso_con::erase成员函数或者remove_copy_if结合swap等方式
有一些比较特殊的容器具现,比如vector等,暂不考虑。
参考
- 关于C++中STL的erase用法
- STL中用erase()方法遍历删除元素
- C++复习之STL(一)—— ERASE和REMOVE特异行为