假定你有一个标准STL容器,c,容纳int,
1 Container<int> c;
而你想把c中所有值为1963的对象都去掉。令人吃惊的是,完成这项任务的方法因不同的容器类型而不同:没有一种方法是通用的。
如果你有一个连续内存容器(vector、deque或string),最好的方法是erase-remove惯用法:
1 c.erase(remove(c.begin(), c.end(), 1963), // 当c是vector、string 2 c.end()); // 或deque时, 3 // erase-remove惯用法 4 // 是去除特定值的元素 5 // 的最佳方法
这方法也适合于list,但是,list的成员函数remove更高效:
1 c.remove(1963); // 当c是list时, 2 // remove成员函数是去除 3 // 特定值的元素的最佳方法
当c是标准关联容器(即,set、multiset、map或multimap)时,使用任何叫做remove的东西都是完全错误的。这样的容器没有叫做remove的成员函数,而且使用remove算法可能覆盖容器值,潜在地破坏容器。
对于关联容器,解决问题的适当方法是调用erase:
1 c.erase(1963); // 当c是标准关联容器时 2 // erase成员函数是去除 3 // 特定值的元素的最佳方法
这不仅是正确的,而且很高效,只花费对数时间。
如果是符合某个判断式的条件
1 bool badValue(int x); // 返回x是否是“bad”
对于序列容器(vector、string、deque和list),我们要做的只是把每个remove替换为remove_if,然后就完成了:
1 c.erase(remove_if(c.begin(), c.end(), badValue), // 当c是vector、string 2 c.end()); // 或deque时这是去掉 3 // badValue返回真 4 // 的对象的最佳方法 5 c.remove_if(badValue); // 当c是list时这是去掉 6 // badValue返回真 7 // 的对象的最佳方法
关联容器:
1 AssocContainer<int> c; 2 ... 3 for (AssocContainer<int>::iterator i = c.begin(); // for循环的第三部分 4 i != c.end(); // 是空的;i现在在下面 5 /*nothing*/ ){ // 自增 6 if (badValue(*i)) c.erase(i++); // 对于坏的值,把当前的 7 else ++i; // i传给erase,然后 8 } // 作为副作用增加i; 9 // 对于好的值, 10 // 只增加i
现在让我们进一步修改该问题。不仅删除badValue返回真的每个元素,而且每当一个元素被删掉时,我们也想把一条消息写到日志文件中。
对于关联容器,这说多容易就有多容易,因为只需要对我们刚才开发的循环做一个微不足道的修改就行了:
1 ofstream logFile; // 要写入的日志文件 2 AssocContainer<int> c; 3 ... 4 for (AssocContainer<int>::iterator i = c.begin(); // 循环条件和前面一样 5 i !=c.end();){ 6 if (badValue(*i)){ 7 logFile << "Erasing " << *i <<'\n'; // 写日志文件 8 c.erase(i++); // 删除元素 9 } 10 else ++i; 11 }
我们必须对vector、string和deque采用不同的战略。特别是,我们必须利用erase的返回值。那个返回值正是我们需要的:一旦删除完成,它就是指向紧接在被删元素之后的元素的有效迭代器。换句话说,我们这么写:
1 for (SeqContainer<int>::iterator i = c.begin(); 2 i != c.end();){ 3 if (badValue(*i)){ 4 logFile << "Erasing " << *i << '\n'; 5 i = c.erase(i); // 通过把erase的返回值 6 } // 赋给i来保持i有效 7 else 8 ++i; 9 }