结论
书里对于要删除容器内元素的情况分成了下面三类:
- 删除特定值的所有对象
- 删除满足特定条件的对象
- 删除对象同时还需要其它操作
对于每一类都有不同的方法来处理, 下面分别介绍。
删除特定值的所有对象
- 对于vector,string或deque使用erase-remove
这一条其实对于连续内存的容器都适用,一般使用下面方法:
c.erase(remove(c.begin(), c.end(), removeVal), c.end);
其中remove是algorithm库中提供的方法,功能是使用move assignment来将不是removeVal的元素移到前面,最后返回前面不是removeVal的元素组成的新范围的end迭代器。然后可以使用容器提供的erase函数来删掉remove返回迭代器后面所有值。
- 对于list,则使用list::remove
list使用上面的方法也是适用的,但是使用list成员函数remove更加有效:
c.remove(removeVal);
- 对于关联容器set/map,则使用set::erase或map::erase
对于关联容器set/map(以及multiset/multimap)既没有remove成员函数,也不能使用remove函数。正确的方法是调用成员函数erase:
c.erase(removeVal);
只删除满足特定条件的对象
这类问题描述是将上面直接给定要删除的值,现在则是删除满足下面条件的对象:
bool badValue(int );
- 对于vector,string或deque使用erase-remove_if, 对于list,则使用list::remove_if
比较简单, 我们可以直接将上面的remove算法换成remove_if:
c.erase(remove_if(c.begin(), c.end(), removeVal), c.end);
c.remove_if(badValue);
- 对于关联容器set/map, 我们不能简单地使用erase, 可以使用remove_copy_if和swap
     AssocContainer<int> c;
     AssocContainer<int> goodValues;
     remove_copy_if(c.begin(), c.end(), 
                   inserter(goodValues, goodValues.end()), 
                   badValue);
     c.swap(goodValues);
这种方法直接简单但是效率比较低,因为要移动元素。其中remove_copy_if功能是将c中满足badValue的元素复制到goodValues中。
或者循环遍历时对于传给erase的迭代器要进行后缀递增
    for(AssocContainer<int>::iterator i = c.begin(); i != c.end(); ) {
      if( badValue(*i) )
        c.erase(i++);
      else
        ++i;
    }
其中关键点就在于传递给erase的迭代器要使用后缀递增, 而不能放在for循环里, 因为删除元素之后,指向该元素的所有迭代器都变得无效。
删除对象同时还需要其它操作
这种情况其实是在上面又加了一条约束, 对于关联容器,我们只需要添加相应操作即可。 而对于顺序容器,则必须采用类似关联容器的方法。
- 对于vector,string,deque或list标准序列容器,需要循环遍历容器中元素,需要注意的是每次调用erase时,需要用返回值更新迭代器
for(SeqContainer<int>::iterator i = c.begin(); i != c.end(); ) {
  if( badValue(*i) )
    // other operation
    i = c.erase(i);
  else
    ++i;
}
关键点在于删除时需要使用erase返回的迭代器来更新循环迭代器
- 对于关联容器set/map,循环遍历时对于传给erase的迭代器要进行后缀递增
for(AssocContainer<int>::iterator i = c.begin(); i != c.end(); ) {
  if( badValue(*i) )
    // other operation
    c.erase(i++);
  else
    ++i;
}
