我正在考虑编写一些可归结为此的代码。 T
是一种集合类型(目前std::map
,如果重要的话)。
T coll;
// ...
T::iterator it, prev;
prev = coll.end();
for(it = coll.begin(); it != coll.end(); ++it) {
if(prev != coll.end())
{ do something involving previous element; }
do something involving this element;
prev = it;
}
我的问题是,将it
复制到这样的prev
是不是一个坏主意?风格不好?可能会在某个地方惹恼学究?可能会因类型T
的细微细节而中断?
我不希望在此循环运行时销毁coll
,或者将任何元素添加到其中或从中删除。
一方面,我所知道的关于迭代器的一切都说这应该是完全安全的。但另一方面,有些东西我不知道迭代器,这感觉有点粗略,所以我想我会问。
附录:
出现这个问题的另一个原因是我的实际代码不会涉及我上面写的for
循环。我实际拥有的是事件驱动的代码;看起来更像是这样:
void onStartDoingSomething() {
it = coll.start();
prev = coll.end();
startOperation(it, prev);
timerStart();
}
void onTimer() {
if(finished with operation on it) {
prev = it;
++it;
startOperation(it, prev);
if(it == coll.end() {
timerStop();
call operation finished;
}
}
}
void startOperation(T::iterator next, T::iterator prev) {
if(prev != coll.end()) {
finish operation on prev;
}
if(next != coll.end()) {
start operation on next;
}
}
所以说明我的问题的另一种方式可能是,“你是否必须使用迭代器,集合类的begin()
和end()
方法,只能在straitlaced常规for
循环中使用,或者你可以任意使用它们吗?for
循环中是否存在任何状态,或者迭代器中是否存在状态?现在,我知道没有什么比“状态存储在for
循环中”,据我所知,迭代器包含所需的所有状态至关重要。事实上,如果迭代器不仅包含它所需的所有状态,而且100%安全可复制,那么我原来问题的答案就是“不,这不是一个坏主意”。但是,迭代器可能不是100%安全可复制的潜在可能性 - 例如,如果复制迭代器时出现细微的别名问题,增加原始值,然后尝试使用副本 - 这就是为什么这段代码感觉永远如此 - 对我来说有点粗略。
答案 0 :(得分:4)
这取决于迭代器的类型,但它对std::map::iterator
来说很好。
迭代器分为不同的类别,具体取决于它们支持的操作以及它们提供的关于它们所引用的值的保证。
std::map::iterator
满足BidirectionalIterator
概念的要求。这意味着,除其他外,递增迭代器的副本不会使原始文件无效。这意味着执行prev = it; ++it
不会使prev
无效,因此您的算法定义明确。
然而,并非所有迭代器都是如此。 InputIterator
概念不提供此保证。请注意++i
的后置条件:
后置条件:以前
i
值的任何副本不再需要可解除引用或位于==
的域中。
我不知道标准库中的任何迭代器如果递增它们的副本会丢失它们的值,但我个人构建了这样的迭代器类型(它遍历存档文件中的条目,当你前进到下一个条目时,前一个条目不再可读。)
另请参阅Iterator
,ForwardIterator
,RandomAccessIterator
和OutputIterator
概念。所有这些都是相互依赖的。
答案 1 :(得分:0)
虽然不明确,但我通常不鼓励存储迭代器。
首先,迭代器概念旨在抽象您正在访问的内容。这可以是蒸汽或容器。 Steam迭代器肯定不稳定,存储它们不会产生有效的代码。但只要容器没有改变,将迭代器存储到容器中是安全的。
这是第二个问题,如果您正在访问容器并且只是从中读取,那么您的代码就可以了。如果您的代码在调用之间改变容器,则迭代器可能会失效。当迭代器失效时,不同的容器具有不同的语义,但我的经验法则是容器调整大小后所有迭代器都无效。
(我不倾向于记住确切的语义,因为我假设我可以随时更改容器并且向量在调整大小时使所有迭代器无效。)