1. 概述
本文讨论序列容器删除特定元素的方法。
2. 示例1:“恰好正确”的例子
2.1 代码
#include <iostream>
#include <vector>
static void init(std::vector<int> &values)
{
values.push_back(1);
values.push_back(2);
values.push_back(4);
values.push_back(4);
values.push_back(5);
}
static void dump(const std::vector<int> &values)
{
std::vector<int>::const_iterator i(values.begin());
std::vector<int>::const_iterator iEnd(values.end());
for (; i != iEnd; ++i) {
std::cout<<*i<<std::endl;
}
}
void remove_elements()
{
std::vector<int> values;
init(values);
std::vector<int>::iterator i(values.begin());
std::vector<int>::iterator iEnd(values.end());
for (; i != iEnd; ) {
if (*i == 4) {
i = values.erase(i);
} else {
++i;
}
}
dump(values);
}
int main()
{
remove_elements();
return 0;
}
2.2 运行结果
flying-bird@flyingbird:~/examples/cpp/stl$ g++ remove_elements.cc
flying-bird@flyingbird:~/examples/cpp/stl$ ./a.out
1
2
5
flying-bird@flyingbird:~/examples/cpp/stl$
一切看来还算正常。
3. 无法运行的代码
3.1 代码
我们现在对上面的代码做一点小小的修改:上面删除的元素是4,现在我们改成删除值等于5的元素。即对应的代码如下:
for (; i != iEnd; ) {
if (*i == 5) {
i = values.erase(i);
} else {
++i;
}
}
3.2 运行结果
下面是此时的运行结果:
flying-bird@flyingbird:~/examples/cpp/stl$ g++ remove_elements.cc
flying-bird@flyingbird:~/examples/cpp/stl$ ./a.out
Segmentation fault
flying-bird@flyingbird:~/examples/cpp/stl$
原因在哪里呢?关键部分代码拷贝如下:
std::vector<int>::iterator i(values.begin());
std::vector<int>::iterator iEnd(values.end());
for (; i != iEnd; ) {
if (*i == 5) {
i = values.erase(i);
} else {
++i;
}
}
一开始初始化了容器的迭代器的最后一个位置,即iEnd。但是在for循环的过程中,使用erase()删除了最后一个元素,由此导致该容器的迭代器的最后一个位置发生了变化。即初始化时候的iEnd已经无效了,由此导致i != iEnd出现了异常。
因为这个原因,可以把代码修改如下:
std::vector<int>::iterator i(values.begin());
for (; i != values.end(); ) {
if (*i == 5) {
i = values.erase(i);
} else {
++i;
}
}
这个时候执行就正常了:
flying-bird@flyingbird:~/examples/cpp/stl$ g++ remove_elements.cc
flying-bird@flyingbird:~/examples/cpp/stl$ ./a.out
1
2
4
4
flying-bird@flyingbird:~/examples/cpp/stl$
4. 继续分析
当然,推荐的方法是《Effective STL》第9条的方法,使用erase-remove方法,即:
#include <algorithm>
void remove_elements()
{
std::vector<int> values;
init(values);
values.erase(std::remove(values.begin(), values.end(), 5), values.end());
dump(values);
}
使用这种方法,删除4或者5都是没有问题的:
flying-bird@flyingbird:~/examples/cpp/stl$ g++ remove_elements.cc
flying-bird@flyingbird:~/examples/cpp/stl$ ./a.out
1
2
5
flying-bird@flyingbird:~/examples/cpp/stl$ g++ remove_elements.cc
flying-bird@flyingbird:~/examples/cpp/stl$ ./a.out
1
2
4
4
flying-bird@flyingbird:~/examples/cpp/stl$
5. 其他
要针对序列中的每个元素施行某个操作,可以使用for_each(),这种代码看起来更加清晰。为此,把上面示例代码中的dump()优化如下:
static void print(int i)
{
std::cout<<i<<std::endl;
}
static void dump(const std::vector<int> &values)
{
std::for_each(values.begin(), values.end(), print);
}