Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do C++ iterators hold a reference to the underlying object?

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'.)

like image 977
Malvineous Avatar asked Aug 18 '16 09:08

Malvineous


3 Answers

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++.

like image 161
Lightness Races in Orbit Avatar answered Oct 19 '22 04:10

Lightness Races in Orbit


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.

like image 20
Johann Gerell Avatar answered Oct 19 '22 04:10

Johann Gerell


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.

like image 2
Sergey Avatar answered Oct 19 '22 04:10

Sergey