C++ 用迭代器遍历vector并erase元素时的潜在问题

问题表述

  1. 在C++对,对于vector经常需要遍历其成员,然后按照某种规则把部分成员删除掉,这里就会用到erase(当然有其他方法,暂不赘述)。但是,如果要移除最后一个元素时,便要注意一些细节,不然便会产生野指针,导致程序segment fault,如下代码中的IterVecNotSafe函数,

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    void IterVecNotSafe() {
        vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8};
        int cnt = 0;
        for (auto iter = vec.begin(); iter != vec.end(); ++iter) {
            if (*iter == 8) {
                vec.erase(iter);
            }
    
            std::cout << "not safe *iter = " << *iter << ", &iter = " << &*iter << ", ++iter addr: " << &*(iter + 1)
                    << std::endl;
    
            if (cnt++ > 11) exit(EXIT_FAILURE);
        }
    }
    
    void IterVecSafe() {
        vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8};
    
        for (auto iter = vec.begin(); iter != vec.end();) {
            // true可以换成所需要的condition
            if (*iter == 8) {
                iter = vec.erase(iter);
            } else {
                iter++;
            }
            // 迭代器取地址的方式&*iter 或者 std::addressof(*iter)
            std::cout << "safe *iter = " << *iter << ", &iter = " << &*iter << ", ++iter addr: " << &*(++iter) << std::endl;
        }
    }
    
    int main(int argc, char* argv[]) {
        IterVecNotSafe();
    
        IterVecSafe();
    
        return 0;
    }
    
    • 程序输出为:
      not safe *iter = 1, &iter = 0x1b83c20, ++iter addr: 0x1b83c24
      not safe *iter = 2, &iter = 0x1b83c24, ++iter addr: 0x1b83c28
      not safe *iter = 3, &iter = 0x1b83c28, ++iter addr: 0x1b83c2c
      not safe *iter = 4, &iter = 0x1b83c2c, ++iter addr: 0x1b83c30
      not safe *iter = 5, &iter = 0x1b83c30, ++iter addr: 0x1b83c34
      not safe *iter = 6, &iter = 0x1b83c34, ++iter addr: 0x1b83c38
      not safe *iter = 7, &iter = 0x1b83c38, ++iter addr: 0x1b83c3c
      not safe *iter = 8, &iter = 0x1b83c3c, ++iter addr: 0x1b83c40
      not safe *iter = 0, &iter = 0x1b83c40, ++iter addr: 0x1b83c44
      not safe *iter = 0, &iter = 0x1b83c44, ++iter addr: 0x1b83c48
      not safe *iter = 1041, &iter = 0x1b83c48, ++iter addr: 0x1b83c4c
      not safe *iter = 0, &iter = 0x1b83c4c, ++iter addr: 0x1b83c50
      not safe *iter = 544501614, &iter = 0x1b83c50, ++iter addr: 0x1b83c54
      
      safe *iter = 3, &iter = 0x1dfbc28, ++iter addr: 0x1dfbc28
      safe *iter = 5, &iter = 0x1dfbc30, ++iter addr: 0x1dfbc30
      safe *iter = 7, &iter = 0x1dfbc38, ++iter addr: 0x1dfbc38
      safe *iter = 0, &iter = 0x1dfbc40, ++iter addr: 0x1dfbc40
      
    • 可以发现,IterVecNotSafe函数删除最后一个元素后产生了野指针

分析

  1. vector::erase官方说明

    • erase支持一次删除某个位置的元素,也支持删除一个区间的元素
    • 注意返回值的说明
      • 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. Member type iterator is a random access iterator type that points to elements.
    • 也就是,当删除最后一个元素时,新的迭代器将指向vector.end(),而对于vector来说,end()都是属于尾后迭代器(off-the-end iterator),指向的是尾元素的下一个位置,而这个位置根本不存在,只是一个标记而已;如果这时候再执行++iter的操作,将导致错误,如输出结果所示
  2. 解决方法

    • 常用的一种解决方案是如上面代码中的IterVecSafe函数,将erase后的结果再返回给迭代器,如此,当删除最后一个元素时,iter将触发iter==vec.end()条件,然后终止循环
posted @ 2020-04-29 01:15  StoneclutterX  阅读(936)  评论(0编辑  收藏  举报