dgmulf
dgmulf

Reputation: 485

C++ vector iterators vs. pointers

There are so many alternative ways of addressing elements of a vector.

I could use a pointer like so:

vector<int> v = {10, 11, 12};
int *p = &v[0];
cout << *p;    //Outputs "10"

I could use a pointer this way too:

vector<int> v = {10, 11, 12};
vector<int>::pointer p = v.data();
cout << *p;    //Outputs "10"

I could also use the iterator type:

vector<int> v = {10, 11, 12};
vector<int>::iterator i = v.begin();
cout << *i;    //Outputs "10"

Are there any significant differences that I'm missing here?

Upvotes: 24

Views: 12178

Answers (4)

pseudovella
pseudovella

Reputation: 238

According to cppreference:

A pointer to an element of an array satisfies all requirements of LegacyContiguousIterator

which is the most powerful iterator as it encompasses all other iterators functionality. So they can be one and the same, an iterator is just a means of making our code clear, consice and portable.

For example we could have some container "C"...

//template <typename T, int N> class C { //for static allocation
template <typename T> class C {
    //T _data[N]; //for static allocation
    T* _data; //need to dynamically allocate _data
public:
    typedef T* iterator;
}

where C<int>::iterator would be an int* and there would be no difference.

Maybe we don't want/need the full power of a LegacyContiguousIterator so we could redefine C<int>::iterator as another class that follows the outline for say LegacyForwardIterator. This new iterator class may redefine operator*. In this case it is implementation dependant and an int* may cause undefined behaviour when trying to access the elements.

This is why iterators should be preferred but in most cases they are going to be the same thing.

In both cases our container " C" will work just like other STL containers so long as we define all the other necessary member functions and typedefs.

Upvotes: 0

Joseph Mansfield
Joseph Mansfield

Reputation: 110658

As far as being able to perform the task at hand, they all work equally well. After all, they all provide an object which meets the requirements of an iterator and you are using them to point at the same element of the vector. However, I would pick the vector<int>::iterator option because the type is more expressive about how we intend to use it.

The raw pointer type, int*, tells you very little about what p is, except that it stores the address of an int. If you think about p in isolation, its type doesn't tell you very much about how you can use it. The vector<int>::pointer option has the same issue - it just expresses the type of the objects it points at as being the element type of a vector. There's no reason it actually needs to point into a vector.

On the other hand vector<int>::iterator tells you everything you need to know. It explicitly states that the object is an iterator and that iterator is used to point at elements in a vector<int>.

This also has the benefit of being more easily maintainable if you ever happen to change the container type. If you changed to a std::list, for example, the pointer type just wouldn't work any more because the elements are not stored as a contiguous array. The iterator type of a container always provides you with a type you can use to iterate over its elements.


When we have Concepts, I'd expect the best practise to be something like:

ForwardIteratorOf<int> it = std::begin(v);

where ForwardIteratorOf<int> (which I am imagining exists) is changed to whatever concept best describes your intentions for it. If the type of the elements doesn't matter, then just ForwardIterator (or BidirectionalIterator, RandomAccessIterator, or whatever).

Upvotes: 14

R Sahu
R Sahu

Reputation: 206577

If you add the check:

if ( !v.empty() )

Then, all the example you've shown are equally valid.

If you are about to iterate over the elements of the vector, I would go with:

vector<int>::iterator i = v.begin();

It's easier to check whether the iterator has reached the end of the vector with an iterator than with the other forms.

if ( i != v.end() )
{
   // Do stuff.
}

Upvotes: 4

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17415

All these ways have their advantages, but at the core they are very similar. Some of them don't work though (they cause so-called "undefined behaviour") when the vector is empty.

Upvotes: 1

Related Questions