【问题标题】:STL How to save iterator after erase()STL擦除后如何保存迭代器()
【发布时间】:2015-03-29 19:32:09
【问题描述】:
  1. 我想处理vector<int>容器中的每个节点。

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

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

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

  5. 这是我的问题:

Q1) 当vector<int>::iterator oldIter = iter;被执行时, oldIter 是否创建新的迭代器对象?

Q2) 为什么迭代器自增后的值不变?

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

Q4) 最后,一个微不足道的问题。我试过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;
}

}

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

    当vector&lt;int&gt;::iterator oldIter = iter;被执行时,oldIter 创建一个新的迭代器对象?

    是的。

    为什么迭代器的值自增后保持不变 是吗?

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

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

    捕获erase调用的返回值。

    iter = vContainer.erase(iter);
    

    最后,一个小问题。我试过 cout

    因为没有operator&lt;&lt; 重载,它在左侧采用std::ostream,在右侧采用向量迭代器。与printf 不同,std::ostream::operator&lt;&lt; 是类型安全的,因此您将收到编译时错误,而不是运行时未定义行为。

    【讨论】:

    • 感谢您的重播。对于问题(3),返回值指向哪里?以前,它指向“2”,但现在它消失了。
    • @PeterHwang:它将指向移动到已擦除位置的元素。你的情况是3。如果删除最后一个元素,它将返回通过调用 end() 返回的迭代器。
    • 如果我理解正确,erase() 删除迭代器指向的节点并将所有剩余的节点移动一个。对吗?
    【解决方案2】:

    理想情况下,不要单独删除节点。相反,将std::remove_if() 与std::vector&lt;...&gt;::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&lt;int&gt;::iterator oldIter = iter 显然创建了iter 的副本。但是请注意,在使用 erase(it) 时,所有引用 it 引用的对象或跟随该对象的迭代器都将失效。
    2. 递增迭代器时,它不会停留在同一位置。如果您认为确实如此,则您可能错误地确定了迭代器位置。确定随机访问迭代器的迭代器位置的最佳方法是获取序列开头的偏移量,例如,使用std::distance(v.begin(), it)(假设it 是v.begin() 到v.end() 范围内的迭代器) .
    3. 只保留从一开始的偏移并恢复它。
    4. 无法打印迭代器。它们的价值没有任何意义。您使用printf() 打印了一些东西这一事实并不意味着什么,因为%x 类型只能用于整数值,并且在特定位置将其与不同类型一起使用是未定义的行为。见上文如何获得迭代器位置的合理指示。请注意,std::distance(v.begin(), it) 的结果是某种整数类型:您最好使用 IOStreams 打印它,它会自动找出类型,而不是为 printf() 的格式说明符猜测其类型。

    【讨论】:

      猜你喜欢
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 2021-05-08
      • 1970-01-01
      • 1970-01-01
      • 2013-01-28
      • 1970-01-01
      • 2010-09-30
      相关资源
      最近更新 更多