C++ STL容器遍历删除元素的方法

  上周在做内存池回收的时候,遍历deque(使用了双端队列来管理内存块)的每一个元素,查找满足条件的内存块并进行回收,结果竟然coredump了。
  写了个简单的测试代码:

#include <deque>
#include <algorithm>
#include <iostream>
using namespace std;

int main()
{
    deque<int> deque_int;

    deque_int.push_back(1);
    deque_int.push_back(2);
    deque_int.push_back(3);
    deque_int.push_back(4);
    deque_int.push_back(5);

    for_each(deque_int.begin(), deque_int.end(), [&](int n){ cout << n << " ";});
    cout << endl;

    deque<int>::iterator it;
    for (it = deque_int.begin(); it != deque_int.end(); it++) {
        if ((*it) == 2 || (*it) == 4) {
            deque_int.erase(it);
        }
    }

    cout << "after erase." << endl;
    for_each(deque_int.begin(), deque_int.end(), [&](int n){ cout << n << " ";});
    cout << endl;

    return 0;
}

  上面的程序运行起来起来很OK,嗯,至少从运行结果来看是符合预期的:
  1

  再去看自己内存池部分代码,分析删除的元素是deque的最后一个元素,好吧,把这里也改为删除最后一个元素:

    for (it = deque_int.begin(); it != deque_int.end(); it++) {
        if ((*it) == 5) {
            deque_int.erase(it);
        }
    }

  编译,运行:
  
  2
  
  真的core了!!!

  问题找到了,但是逻辑没问题的呀(嗯,表面上看确实如此)。

  分析,在erase最后一个元素之后,在进入下一次循环之前,it++后it指向的是删除操作之前的deque的end,这样在比较it != deque_int.end()时,你以为应该是false了,实际它还是true。(有一句话这么说,“人不能两次踏进同一条河流”,现在的end,已经不是之前的那个end了)本该结束循环的,结果再次进入了循环,访问了一个非法地址,导致了core. 验证也确实如此。
  
  好,原来竟是这样,那就该找解决办法了。
  在C++ reference里边,deque的erase方法有这样一段:

Return value
An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.

  那么就可以用这个返回值来做点文章:

    for (it = deque_int.begin(); it != deque_int.end(); ) {
        if ((*it) == 2 || (*it) == 3 || (*it) == 5) {
            it = deque_int.erase(it);
            continue;
        }
        it++;
    }

  这回挺和谐了:
  3

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐