我知道代码不是一个好习惯,因此问题不关乎此。我只想了解以下示例的工作方式。注意,当我调用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
答案 0 :(得分:3)
通过删除迭代器所寻址的对象来使迭代器无效(无论您是否为此目的而使用)。如果在此之后尝试访问它,则该行为是不确定的(读取:可能发生任何事情,例如同一it
跳转到下一个元素,或者程序崩溃)。您不能将其依赖于其他任何行为。
答案 1 :(得分:2)
我的其他答案不正确。观察到的行为归因于reverse_iterator
的实现。来自cppreference:
std::reverse_iterator
是迭代器 adaptor ,可反转给定迭代器的方向。换句话说,std::reverse_iterator
配备双向迭代器后,会生成一个新的迭代器,该迭代器将从底层双向迭代器定义的序列的末尾开始移动。对于由迭代器
r
构造的反向迭代器i
,关系&*r == &*(i-1)
始终为真(只要r
是可解除引用的);因此,从一端到最后的迭代器构造的反向迭代器将引用序列中的最后一个元素。
(重点是我的)。另请参见[reverse.iterator]。
好的,这对我们意味着什么:当反向迭代器it
指向“ tom”时,它实际上将正向迭代器环绕到 next 元素“ bart”。当您取消引用它时,它需要在包装的迭代器之前添加一个元素,即在“ bart”之前添加一个元素,该元素的确是“ tom”。
当您删除“ tom”时,包装的迭代器不会更改。 (它们都没有失效。)它仍然指向“ bart”。当您取消引用反向迭代器时,它会查找“ bart”之前的内容,而“
这意味着您并不会真正导致不确定的行为。如果您在第60行打了remove("bart")
,就可以了。