STL擦除后如何保存迭代器()

时间:2015-03-29 19:32:09

标签: c++ stl

  1. 我想处理vector<int>容器中的每个节点。

  2. 假设vector<int>包含0,1,2和3,迭代器指向2。

  3. 我想擦除2而不会丢失迭代器位置的信息,因为我想在擦除2后处理“3”。

  4. 这是我所做的,但输出并没有显示我的意图。

  5. 以下是我的问题:

  6. Q1)执行vector<int>::iterator oldIter = iter;oldIter是否创建了一个新的迭代器对象?

    Q2)为什么迭代器的值在递增后保持不变?

    Q3)擦除节点而不丢失的好方法是什么    迭代器的位置?

    问题4)最后,一个微不足道的问题。我试过cout << iter;,但它不起作用。那是为什么?

    以下是代码:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main(){
    
    vector<int> vContainer;
    vector<int>::iterator iter;
    
    vContainer.push_back(0);
    vContainer.push_back(1);
    vContainer.push_back(2);
    vContainer.push_back(3);
    
    for(int i = 0; i < vContainer.size(); i++){
      cout << vContainer[i] << endl;
    }
    
    printf("iter: %x\n", iter);
    iter = vContainer.begin();
    
    
    // Move the itertor to the 2.
    while(*iter != 2){
        iter++;
    }
    
    printf("iter: %x\n", iter);
    
    vector<int>::iterator oldIter = iter;
    printf("\nBefore erase(oldIter)\n");
    printf("oldIter: %x\n", oldIter);
    printf("iter: %x\n", iter);
    
    iter++;
    
    printf("\nAfter incrementing iter\n");
    printf("oldIter: %x\n", oldIter);
    printf("iter: %x\n", iter);
    
    vContainer.erase(oldIter);
    
    printf("\nAfter erase(oldIter)\n");
    printf("oldIter: %x\n", oldIter);
    printf("iter: %x\n", iter);
    
    for(int i = 0; i < vContainer.size(); i++){
      cout << vContainer[i] << endl;
    }
    
    }
    

2 个答案:

答案 0 :(得分:2)

  

执行vector<int>::iterator oldIter = iter;时,是否为oldIter   创建一个新的迭代器对象?

  

为什么递增后迭代器的值保持不变   它?

它没有。是什么让你得出结论呢?你的printf陈述?那些都是无效的。 printf不知道如何处理向量迭代器。您对它的调用是未定义的行为。

  

在不丢失节点位置的情况下擦除节点有什么好方法   迭代器?

捕获erase来电的返回值。

iter = vContainer.erase(iter);
  

最后,一个微不足道的问题。我试过cout&lt;&lt; ITER;但事实并非如此   工作。那是为什么?

因为没有operator<<重载,左边是std::ostream,右边是矢量迭代器。与printf不同,std::ostream::operator<<是类型安全的,因此您将获得编译时错误,而不是运行时未定义的行为。

答案 1 :(得分:2)

理想情况下,单独删除节点。相反,将std::remove_if()std::vector<...>::erase()结合使用:这会将潜在的二次算法转换为线性算法。也就是说,你会使用这样的东西

vContainer.erase(std::remove_if(vContainer.begin(),
                                vContainer.end(),
                                [](int value){ return someCondition(value); }),
                               vContainer.end());

否则,最简单的方法可能只是使用偏移量恢复位置:

int offset(std::distance(vContainer.begin(), it));
vContainer.erase(it);
it = vContainer.begin() + offset;

由于erase(it)将移动位置it之后的所有对象,但它相当昂贵。使用像std::remove_if()这样的算法来聚合更改并将每个元素最多移动一次,而不是每erase()移动一次,效率要高得多。

解决您的具体问题:

  1. std::vector<int>::iterator oldIter = iter显然会创建iter的副本。但请注意,使用it时,引用erase(it)引用的对象或跟随该对象的所有迭代器都将失效。
  2. 递增迭代器时,它不会停留在同一位置。如果您认为它确实可能错误地确定了迭代器位置。确定随机访问迭代器的迭代器位置的最佳方法是从序列的开头获取偏移量,例如,使用std::distance(v.begin(), it)(假设itv.begin()范围内的迭代器} v.end())。
  3. 保持偏离开始并恢复它。
  4. 无法打印迭代器。他们的价值没有任何意义。您使用printf()打印的内容并不意味着什么,因为类型%x只能用于整数值,并且在特定位置使用不同类型的是未定义的行为。请参阅上文,了解如何合理地指示迭代器位置。请注意std::distance(v.begin(), it)的结果是一些整数类型:最好用IOStreams打印它,它会自动计算出类型,而不是为printf()的格式说明符猜测它的类型。