STL中,要删除容器内的一个元素,其方法会随着容器类型的不同而不同。
一、删除特定值的元素
例如,对于STL容器c,c包含int类型的元素,现在需要删除c中等于2014的元素。
Container<int> c;
1. 序列容器
序列容器即具有连续内存的容器,如vector,deque,string,其删除特定值的元素的方法最好是使用erase-remove用法。
// 当c是vector,deque,string,使用erase-remove方法
c.erase(remove(c.begin(), c.end(), 2014), c.end());
对于list,则直接可以调用remove方法。
// 当list时,remove方法是删除特定值元素的最好方法
c.remove(2014);
2. 关联容器
若c是关联容器,即c是set或者map等类型,则正确方法是使用erase方法。
// 若c是关联容器,则erase方法是删除特定值元素的最好方法
c.erase(2014);
二、删除满足特定条件的元素
对于容器c,需要删除使下面判别式为true的每一个元素:
bool predicate(int);
1. 序列容器
对于序列容器,vector,deque,string,list,把每个remove的调用换成调用remove_if就行了:
// 当c是vector,deque,string时,使用erase-remove_if是删除满足特定条件元素的最好方法
c.erase(remove_if(c.begin(), c.end(), predicate), c.end());
// 当c是list时,remove_if是删除满足特定条件元素的最好方法
c.remove_if(predicate);
2. 关联容器
关联容器并没有提供类似remove_if的方法,因此须写一个循环来遍历c中的所有元素,并在遍历过程中删除满足特定条件的元素。
下面的代码是很多程序员首先能想到的。
AssocContainer<int> c;
for (AssocContainer<int>::iterator it = c.begin(); it != c.end(); ++it) {
if (predicate(*it))
c.erase(it);
}
可是,当关联容器中的一个元素被删除时,指向该元素的所有迭代器都将失效。一旦执行了c.erase(it),it就变成无效的值。
为了避免这个问题,我们要确保在调用erase之前,有一个迭代器指向c中的下一个元素。下面的代码演示了这个方法。
AssocContainer<int> c;
for (AssocContainer<int>::iterator it = c.begin(); it != c.end(); ) {
if (predicate(*it))
c.erase(it++);
else
it++;
}
对于序列容器,如果也要通过遍历循环来删除特定的元素,则不能使用上面的删除关联容器特定元素的方法,因为对vector,string,deque这类序列容器来说,调用erase不仅会使指向被删除元素的迭代器失效,而且也会使被删除元素之后的所有迭代器失效。
下面代码演示了序列容器使用遍历循环来删除元素的方法。
SeqContainer<int> c;
for (SeqContainer<int>::iterator it = c.begin(); it != c.end(); /* 啥也不做 */) {
if (predicate(*it))
it = c.erase(it); // 把erase的返回值赋给it,使it的值保持有效
else
++it;
}
值得注意的是,这种 方法仅对序列容器适用。对于关联容器,若erase的参数是迭代器(erase的参数也可以是里面的元素),则返回值是void。所以上面的方法并不适用于关联容器。
参考资料:
《Effective STL》第三版