I can't seem to find much information about whether iterators keep hold of the underlying object they are iterating over.
If I create an iterator, then the object that supplied it goes out of scope, does the presence of the iterator prevent it from being destroyed?
Here is a very simple example just to illustrate the scenario:
// This class takes a copy of iterators to use them later
class Data {
public:
Data(std::vector<int>::iterator start, std::vector<int>::iterator end)
: start(start),
end(end)
{}
void show() {
// Use this->start and this->end for some purpose
}
private:
std::vector<int>::iterator start;
std::vector<int>::iterator end;
};
Data test() {
std::vector<int> v{1, 2, 3};
Data d(v.begin(), v.end());
d.show(); // this would be ok
return d;
}
int main(void) {
Data d = test();
d.show(); // What happens here?
}
In this example, the Data
object is storing a copy of the iterators, which is fine for the first show()
call. However by the time of the second show()
call, the original object that supplied the iterators no longer exists.
Do the iterators keep the object around until such time as they are all themselves destroyed, or are the iterators invalidated as soon as the original object goes out of scope?
Here is one reference of many which doesn't say what happens one way or the other (or even whether the result of this is 'undefined'.)
Iterators typically don't own the data over which they iterate, no. In fact, they're rarely (if ever) even aware of the object that owns the data; vector iterators, for example, are often just pointers, which have no knowledge of any vector or of its lifetime. Even those iterators that are not implemented as pointers (which is most of them) may be considered a kind of "pointer", and treated as such: they can quite easily become dangling.
Your example has UB because you'll dereference invalid iterators inside show()
the second time.
If your container goes out of scope then all your iterators become invalidated. In fact, there are all manner of reasons why an iterator may become invalidated, such as adding to a vector when that operation results in a capacity expansion.
It's possible to find iterators that do kind of "own" data, instead of iterating over some collection found elsewhere (such as Boost's counting iterators), but these are magical properties that take advantage of C++ to provide a magical function, not an inherent property of iterators as defined by C++.
An iterator is generally only valid as long as its originating container or "sequence" has not been changed, because a change might cause memory reallocation and memory moves. Since the iterator usually reference memory in the originating container, a change in said container might invalidate the iterator.
Now, a container that goes out of scope gets its destructor executed. That will obviously change the container and hence any iterator to it will be invalidated in the process.
First, iterator does not have an interface to reference an object it iterates over. It only implements pointer semantics, so you may think of it as of abstract pointer. Of course, it's internal implementation may hold a pointer to that object, but it's very unlikely in real-world implementations.
Second, when your container is destroyed (and it is when it goes out of scope), all objects in the container are being destroyed too. Thus, iterator becomes invalid after you container was destroyed. After that incrementing, decrementing and dereferencing the iterator will cause undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With