对 C++ 迭代器感到困惑
Posted
技术标签:
【中文标题】对 C++ 迭代器感到困惑【英文标题】:Confused with C++ iterator 【发布时间】:2015-07-23 01:25:21 【问题描述】:我有 3 个使用 c++ 迭代器的场景,它们一起让我感到困惑。
这是我的主要代码:
int arr[] = 13,20,40;
set<int> st(arr,arr+3);
auto it=st.begin();
auto tmp=it;
it++;
st.erase(it);
//scenarios here
1-如果我写下面,结果是20,为什么?
cout << *tmp << endl;
2- 如果我向前移动指针,结果是 13,为什么?
tmp++;
cout << *tmp << endl;
3- 如果我向后移动指针,结果也是13,为什么它和向前移动一样?
tmp--;
cout << *tmp << endl;
4- 最后,如果我不是从集合中间擦除某些东西,而是擦除起始项,结果是一个随机数。
auto it=st.begin();
auto tmp = it;
st.erase(it);
cout << *tmp << endl;//result: 13
tmp++;
cout << *tmp << endl;//result: random number
如果您知道任何与此问题相关的有关 c++ 中迭代器的有用链接,请提及。
【问题讨论】:
所有问题的答案都是“未定义的行为”查找iterator invaidation rules erase 将删除指向节点并返回下一个节点的迭代器并分配给它。这就是为什么你得到 20,即下一个节点的值。 @paper.plane,实际上我在擦除部分之前忘记写了++,我编辑了代码。看来您的猜测是错误的,因为 20 是已经删除的迭代器的值。 我同意答案可能在重复的问题中,但我不认为它是重复的,因为答案没有清楚地解释这个案例的具体原因,至少我认为提供的答案太长了,找哪个段落包含这个案例的答案对初学者不友好 @amuse,实际上通过阅读建议链接中的解释,我完全理解了它。当他们说这是“未定义的行为”时,这是完全合理的, 【参考方案1】:如果您删除了迭代器指向的set
中的条目,则您已使迭代器无效。一旦迭代器失效,您就不应再使用它,因为它会导致未定义的行为。
【讨论】:
我认为答案出现在标记为重复的问题之前是幸运的【参考方案2】:首先你没有提到你的编译器。在“我如何让 C++ 执行 X、Y 和 Z”之类的问题中,这是可以的,但在这样的问题中,您会问为什么您的程序没有按照您的预期执行,您应该指定编译器(和版本!)。
原因很简单。首先,可能存在编译器错误,可能会导致某些代码无法执行它应该执行的操作。其次是标准的松散领域。例如在这种情况下,没有提到 set 是如何实现的,只是接口应该如何表现。所以 set 可以实现为树、数组、哈希表等等。这些行为会改变 set 的行为方式,尤其是在“未定义”的情况下。
第二个当你显示多个测试用例时,就像这里一样。将测试用例与宏分开会很有用。
#ifdef TEST1
cout << *tmp << endl;
#endif
#ifdef TEST2
tmp++;
cout << *tmp << endl;
#endif
#ifdef TEST3
tmp--;
cout << *tmp << endl;
#endif
通常您应该避免使用宏,但条件编译是该规则的少数例外之一。
那么现在回答你的问题。
首先。我倾向于主要使用向量,我倾向于将迭代器作为 C 风格的指针和 ++ 和 -- 作为指针算术。很难看出矢量不是这种情况。但并非总是如此。这有点与我的直觉相混淆。尽管如此,这是一幅很好的画面。
请记住,它只是大部分准确。根据 set 的实现方式,迭代器会有所不同。在树的情况下,迭代器将是一个指针,++ 将是 this->next
,而 -- 将是 this->last
。
第二。有两种类型的集合。集合(我称之为基本集合)基于“原始”数据类型,以及虚拟集合,如目录列表或 SQL 结果集。在第一种情况下,几乎总是有一个指针隐藏在迭代器的内部某处(或不那么深)。即使在第二种情况下,也有某种指针之类的对象(文件描述符/句柄,SQL 游标)。因此,尽管迭代器处于未定义状态,但它仍会指向某些东西。虽然那东西可能是无效的。这就像一个指向错误位置的字符指针。那里仍然有角色,他们可能只是垃圾。
第三。要了解为什么在集合更改时迭代器会变得不一致,请考虑如何创建在容器更改后保持一致的迭代器?首先,您需要在容器和迭代器中实现observer pattern,以便让迭代器知道容器何时发生更改。迭代器应该是一个轻量级的对象。仅此一项就会导致迭代器大小翻倍。创建迭代器时,消息传递会增加开销。
标准委员会有一条规则:您不使用的任何功能在不使用时都不应产生开销。这意味着您将需要至少一种方法的单独类来区分容器是否允许在容器更改时保持一致的迭代器。这将意味着 STL 的复杂性更高。
还有如何修复不一致的迭代器的问题。假设迭代器指向的元素被删除。你做什么工作?回去一个?前进一个?如果集合是无序的怎么办?然后更改集合会导致迭代顺序发生更改。如何确保迭代器命中所有节点?
第四。在做这样的事情(教学程序)时,最好使用调试器逐步执行代码以了解内部发生的情况。
抱歉,拖了这么久。但答案比我想象的要复杂。这对 C++ 来说并不新鲜。欢迎使用。
【讨论】:
以上是关于对 C++ 迭代器感到困惑的主要内容,如果未能解决你的问题,请参考以下文章