Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to hold pointers to iterators in C++?

I will ask the question first and the motivation next, and finally an illustrative code sample which compiles and executes as expected.

Question

If I can assure myself that an iterator will not get invalidated in the duration when I will be needing to use it, is it safe to hold a pointer to an iterator (e.g. a pointer to a list<int>::iterator).

Motivation

I have multiple containers and I need direct cross references from items held in one container to the corresponding items held in another container and so on. An item in one container might not always have a corresponding item in another container.

My idea thus is to store a pointer to an iterator to an element in container #2 in the element stored in container #1 and so forth. Why? Because once I have an iterator, I can not only access the element in container #2, but if needed, I can also erase the element in container #2 etc.

If there is a corresponding element in container #2, I will store a pointer to the iterator in the element in container #1. Else, this pointer will be set to NULL. Now I can quickly check that if the pointer to the iterator is NULL, there is no corresponding element in container #2, if non-NULL, I can go ahead and access it.

So, is it safe to store pointers to iterators in this fashion?

Code sample

#include <iostream>
#include <list>

using namespace std;

typedef list<int> MyContainer;
typedef MyContainer::iterator MyIterator;
typdef MyIterator * PMyIterator;

void useIter(PMyIterator pIter)
{
    if (pIter == NULL)
    {
    cout << "NULL" << endl;
    }
    else
    {
    cout << "Value: " << *(*pIter) << endl;
    }
}

int main()
{
    MyContainer myList;

    myList.push_back(1);
    myList.push_back(2);

    PMyIterator pIter = NULL;

    // Verify for NULL
    useIter(pIter);

    // Get an iterator
    MyIterator it = myList.begin();

    // Get a pointer to the iterator
    pIter = & it;

    // Use the pointer
    useIter (pIter);
}
like image 867
kman Avatar asked Dec 20 '12 12:12

kman


People also ask

Why use iterators instead of pointers?

After all, iterators are invalidated at mostly the same times and the same ways as pointers, and one reason that iterators exist is to provide a way to "point" at a contained object. So, if you have a choice, prefer to use iterators into containers.

What is a good reason to use an iterator instead of a cursor in a container class?

The primary purpose of an iterator is to allow a user to process every element of a container while isolating the user from the internal structure of the container.

Do all iterators use pointers?

A pointer can point to elements in an array and can iterate through them using the increment operator (++). But, all iterators do not have similar functionality as that of pointers.

Are iterators pointers or references?

Iterators are a generalization of pointers that allow a C++ program to work with different data structures (containers) in a uniform manner [ISO/IEC 14882-2014].


4 Answers

Iterators are generally handled by value. For instance, begin() and end() will return an instance of type iterator (for the given iterator type), not iterator& so they return copies of a value every time.

You can of course take an address to this copy but you cannot expect that a new call to begin() or end() will return an object with the same address, and the address is only valid as long as you hold on to the iterator object yourself.

std::vector<int> x { 1, 2, 3 };

// This is fine:
auto it = x.begin();
auto* pi = &it;

// This is not (dangling pointer):
auto* pi2 = &x.begin();

It rarely makes sense to maintain pointers to iterators: iterators are already lightweight handles to data. A further indirection is usually a sign of poor design. In your example in particular the pointers make no sense. Just pass a normal iterator.

like image 115
Konrad Rudolph Avatar answered Nov 23 '22 23:11

Konrad Rudolph


The problem with iterators is that there are a lot of operations on containers which invalidate them (which one depend on the container in question). When you hold an iterator to a container which belongs to another class, you never know when such an operation occurs and there is no easy way to find out that the iterator is now invalid.

Also, deleting elements directly which are in a container which belongs to another class, is a violation of the encapsulation principle. When you want to delete data of another class, you should better call a public method of that class which then deletes the data.

like image 21
Philipp Avatar answered Nov 23 '22 23:11

Philipp


Yes, it is safe, as long as you can ensure the iterators don't get invalidated and don't go out of scope.

like image 32
NPE Avatar answered Nov 24 '22 01:11

NPE


Sounds scary. The iterator is an object, if it leaves scope, your pointer is invalid. If you erase an object in container #2, all iterators may become invalid (depending on the container) and thus your pointers become useless.

Why don't you store the iterator itself? For the elements in container #1 that don't refer to anything, store container2.end(). This is fine as long as iterators are not invalidated. If they are, you need to re-generate the mapping.

like image 23
Tannin Avatar answered Nov 24 '22 01:11

Tannin