remove和erase函数
In total: remove
函数在删除元素的时候,需要小心使用。
Description of algorithm : 是查找的得到第一个元素的位置,然后从此位置开始遍历容器,将后面的元素依次前移,跳过和value
相同值的元素。而最后返回的是指向的最后一个有效的元素的迭代器。
Look out!: 在remove
算法过程中,并不会修改原容器的size()
,以及end()
等函数。因为当没有具体remove()
是哪一个容器的函数时,他是不会修改原容器的相关系数的值,除非如这样明确使用list.remove(1)
等。
从逻辑角度看,最后的[ 从remove
得到的iterator
—_result
, 容器的结尾end()
) 这个区间里面的元素已经没有意义了。
如以下实现:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
template <class _ForwardIterator, class T>
_ForwardIterator Remove (_ForwardIterator first, _ForwardIterator last, const T& val)
{
_ForwardIterator result = first;
while (first!=last) {
if (!(*first == val)) {
*result = move(*first);
++result;
}
++first;
}
return result;
}
int main()
{
typedef vector<char> Vector;
Vector vec;
vec.push_back('A');
vec.push_back('A');
vec.push_back('A');
vec.push_back('A');
vec.push_back('A');
vec.push_back('C');
vec.push_back('C');
vec.push_back('C');
vec.push_back('C');
vec.push_back('C');
vec.push_back('A');
Vector::iterator p = Remove(vec.begin(),vec.end(),'A');
cout << vec.end() - p << endl;
for(Vector::iterator ite = vec.begin() ; ite != vec.end() ; ++ite)
cout << *ite << " " ;
return 0 ;
}
执行效果
或者
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
typedef vector<char> Vector;
Vector vec;
vec.push_back('A');
vec.push_back('A');
vec.push_back('A');
vec.push_back('A');
vec.push_back('A');
vec.push_back('C');
vec.push_back('C');
vec.push_back('C');
vec.push_back('C');
vec.push_back('C');
vec.push_back('A');
Vector::iterator p = remove(vec.begin(),vec.end(),'A');
cout << vec.end() - p << endl;
for(Vector::iterator ite = vec.begin() ; ite != vec.end() ; ++ite)
cout << *ite << " " ;
return 0 ;
}
执行效果
两者的效果是一样的,那是因为result
在进行记录筛选的时候,开始的迭代器指向的是first
,那么如例子A A A A A C C C C C A
中,将后面五个C
筛选出来之后,此时的迭代器相当于停留在A A A A A C C C C C A
中的第一个C
的位置,那么所以在输出结果的时候,不仅有筛选出来的结果C C C C C
,还会有原来列表A A A A A C C C C C A
中的第一个C
以及后面的元素C C C C C A
,所以输出之后就是C C C C C C C C C C A
。那么为了避免这种效果出现,经常在后面补上vec.erase(remove(vec.begin(),vec.end(),'A'),vec.end());
用来删除后续无用的数据。
所以remove()
只是将待删除元素之后的元素移动到vector
的前端,而不是删除。若要真正移除,需要搭配使用erase()
。
那么使用erase()
函数也有注意点:
for(ite=vec.begin();ite!=vec.end();){
if(*ite == 'A')
vec.erase(ite);
// else
// ++ite;
}
这样使用是错误的,因为在erase()
之后,ite就是野指针了
而下列这种情况不会删除连续相等的数值,因为erase()
返回的是被删除元素的下一个迭代器:
for(ite=vec.begin();ite!=vec.end();){
if(*ite == 'A')
ite = vec.erase(ite);
// else
// ++ite;
}
所以常用一下形式
for (ite = vec.begin(); ite != vec.end(); ) {
if (*ite == 'A')
ite = vec.erase(ite);
else
++ite;
}
概述一下其他迭代器的失效情况:
vector
vector
容器,当插入(push_back)一个元素后,end()
操作返回的迭代器失效,因为他会更新容器,扩大容器。- 当进行删除操作(
erase()
,pop_back()
)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。其实这个也挺好理解的,进行了删除,被删除的指向的迭代器肯定失效啊,因为vector容器是动态调整大小的,有元素被删除了,那么后面的元素肯定前移啊。 - 当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效,这个应该是扩容的原因。
deque
- 在deque容器首部或者尾部插入元素不会使得任何迭代器失效。
- 在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效,如上面所述的那一个例子。
- 在deque容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器失效。因为双向队列元素是相邻关系,而不是靠指针相连的关系。
list
增加任何元素都不会使迭代器失效。删除元素时,除了指向当前被删除元素的迭代器外,其它迭代器都不会失效,因为删除的元素是依靠指针相连的,所以删除与插入都不会影响邻接关系。
set
如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。因为他是自动排序的。
map
如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。因为他是自动排序的。
部分资料有所参考网络资料。