STL 如何在 erase() 之后保存迭代器

STL How to save iterator after erase()

  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;
}

}

When vector<int>::iterator oldIter = iter; is executed, Does oldIter create a new iterator object?

是的。

Why does the value of the iterator remain the same after incrementing it?

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

What is a good way to erase a node without losing the location of the iterator?

捕获 erase 调用的 return 值。

iter = vContainer.erase(iter);

Finally, a trivial question. I tried cout << iter; but it does not work. Why is that?

因为没有 operator<< 重载,它在左侧采用 std::ostream,在右侧采用向量迭代器。与 printf 不同的是,std::ostream::operator<< 是类型安全的,所以你会得到一个编译时错误,而不是运行时未定义的行为。

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