0

我知道代码不是好的做法,所以问题不在于这个。我只想了解以下示例的工作原理。请注意,当我调用 remove 时,我没有对迭代器做任何事情,所以当循环进入下一次迭代时,它是如何指向下一个元素的?

#include <string>
#include <list>
#include <algorithm>
#include <iostream>

class Obj;
std::list<Obj> objs;

class Obj
{
public:
  Obj(const std::string& name, int age)
  : name_(name), age_(age)
  {}

  std::string name()
  {
    return name_;
  }

  int age()
  {
    return age_;
  }
private:
  std::string name_;
  int age_;
};


void remove(const std::string& name)
{
  auto it = find_if(objs.begin(), objs.end(),[name] (Obj& o) { return (o.name() == name); });
  if (it != objs.end())
  {
    std::cout << "removing " << it->name() << std::endl;
    objs.erase(it);
  }
}

int main()
{
  objs.emplace_back("bob", 31);
  objs.emplace_back("alice", 30);
  objs.emplace_back("kevin", 25);
  objs.emplace_back("tom", 45);
  objs.emplace_back("bart", 37);
  objs.emplace_back("koen", 48);
  objs.emplace_back("jef", 23);
  objs.emplace_back("sara", 22);

  auto it = objs.rbegin();
  while (it != objs.rend())
  {

   std::cout << it->name() << std::endl;

   if (it->name() == "tom")
   {
      remove(it->name()); //notice I don't do anything to change the iterator
   }
   else
   {
     ++it;
   }
  }
  return 0;
}

以下是输出:

sara
jef
koen
bart
tom
removing tom
kevin
alice
bob
4

2 回答 2

3

您可以通过删除它所寻址的对象来使迭代器无效(无论您是否为此目的使用它的值)。如果您在那之后尝试访问它,则行为是未定义的(阅读:任何事情都可能发生,例如相同的it跳转到下一个元素,或者您的程序崩溃)。您不能将其依赖于任何其他行为。

于 2018-09-07T09:31:24.333 回答
2

我的另一个答案是不对的。观察到的行为是由于reverse_iterator. 从cppreference

std::reverse_iterator是一个迭代器适配器,它反转给定迭代器的方向。换句话说,当提供双向迭代器时,std::reverse_iterator会生成一个新迭代器,该迭代器从底层双向迭代器定义的序列的末尾移动到开头。

r对于从 iterator 构造的反向迭代器i,关系&*r == &*(i-1)始终为真(只要r是可解引用的);因此,从一个过去的迭代器构造的反向迭代器取消对序列中最后一个元素的引用。

(强调我的)。另见[reverse.iterator]

好的,这对我们意味着什么:当反向迭代器it指向“tom”时,它实际上将前向迭代器环绕到下一个元素“bart”。当你取消引用它时,它在被包装的迭代器之前有一个元素,即在“bart”之前的一个元素,它确实是“tom”。

当您删除“tom”时,包装的迭代器不会改变。(也没有失效。)它仍然指向“bart”。当您取消引用反向迭代器时,它会查找“bart”之前的内容,现在是“kevin”。

这意味着您不会真正导致未定义的行为。如果你remove("bart")在第 60 行打电话,你会的。

于 2018-09-07T12:59:03.217 回答