How Are Iterators and Pointers Related

How are iterators and pointers related?

Iterators are a generalization of pointers.

An iterator (depending on the variants) have to implement * and ++

So a pointer IS an iterator. But not necessarily the other way round.

If you want to iterate over a complex structure (a tree, a graph...), the iterator will be much more than a pointer, and doesn't make any reference to some actual place in the ram.

Is an iterator in C++ a pointer?

The short answer is:

  • Pointer is a kind of iterator.
  • Pointer can be therefore used as an iterator.
  • Pointer has properties other than iterator.

History

Historically, we have C pointer, and it is adapted into C++ when C++ is invented. Pointer represents a location in memory, therefore can be used as a location in an array.

Later, in 1990s, an idea called "iterator concept" is introduced to C++. The "iterator concept" is related to a library called STL (which is later absorbed into the Standard Library) and a paradigm called "generic programming". The iterator concept is inspired from C pointer to represent a location in containers like vector, deque and others, just like how C pointer represent location in array. The iterator concept is carefully engineered to be compatible with C pointer, hence we can nowadays say C pointer models iterator concept.

Iterator concept

A simplified way to understand iterator concept is that, if a data type suppports a list of operations and behaviours, such that it represent a location in a container, and enable some kind of access to the element, it can be called an iterator.

With carefull design of iterator concept, C pointer fulfill that list. Pointer is therefore a kind of iterator.

Iterator concept being just a set of requirement on types, means that you can create your own iterator through C++ power of data abstraction.

Other properties of pointer

Pointer exhibits other properties, and they are not related to iterator concept.

A significant use of pointer is to express reference semantics, i.e. to refer to an object in a remote memory location. This usage of pointer is later considered unsafe, and causes the invention of "smart pointer". By comparing smart pointers and iterators, we can find that they are totally unrelated concepts.

Another use of pointer is to refer to a raw memory location. This is completely unsafe for application programming, but is a essential tool for microcontroller programming to manipulate hardware.

Are all pointers considered iterators?

"Iterator" is some abstract concept, describing a certain set of operations a type must support with some specific semantics.

Pointers are iterators because they fulfill the concept iterator (and, even stronger, random access iterator), e.g. the operator++ to move to the next element and operator * to access the underlying element.

In your particular example, you get a standard iterator range with

[p, p+1)

which can be used for example in the standard algorithms, like any iterator pair. (It may not be particularly useful, but it is still valid.) The above holds true for all "valid" pointers, that is pointers that point to some object.

The converse implication however is false: For example, consider the std::list<T>::iterator. That is still an iterator, but it cannot be a pointer because it does not have an operator[].

How is iterator different from a pointer during the deallocation of memory in C++

Once you call mapExpSessData.erase(tmp_sessionId);, the node in the map that itr_del points to has been deleted. Calling delete itr_del->second; is then Undefined Behavior because you try to access deallocated memory.

You need to delete the value in the map first, then delete the node in the map:

delete itr_del->second;
mapExpSessData.erase(itr_del);

The code block the iterator is in should be small at this point, and the iterator itself going out of scope so you shouldn't need to do anything with it to clear it out. But if you really want to you can assign the default value for its type back to it.

Prefer Iterators Over Pointers?


Introduction

There are many perks of using iterators instead of pointers, among them are:

  • different code-path in release vs debug, and;
  • better type-safety, and;
  • making it possible to write generic code (iterators can be made to work with any data-structure, such as a linked-list, whereas intrinsic pointers are very limited in this regard).




Debugging

Since, among other things, dereferencing an iterator that is passed the end of a range is undefined-behavior, an implementation is free to do whatever it feels necessary in such case - including raising diagnostics saying that you are doing something wrong.

The standard library implementation, libstdc++, provided by gcc will issues diagnostics when it detects something fault (if Debug Mode is enabled).


Example

#define _GLIBCXX_DEBUG 1 /* enable debug mode */

#include <vector>
#include <iostream>

int
main (int argc, char *argv[])
{
std::vector<int> v1 {1,2,3};

for (auto it = v1.begin (); ; ++it)
std::cout << *it;
}
/usr/include/c++/4.9.2/debug/safe_iterator.h:261:error: attempt to 
dereference a past-the-end iterator.

Objects involved in the operation:
iterator "this" @ 0x0x7fff828696e0 {
type = N11__gnu_debug14_Safe_iteratorIN9__gnu_cxx17__normal_iteratorIPiNSt9__cxx19986vectorIiSaIiEEEEENSt7__debug6vectorIiS6_EEEE (mutable iterator);
state = past-the-end;
references sequence with type `NSt7__debug6vectorIiSaIiEEE' @ 0x0x7fff82869710
}
123

The above would not happen if we were working with pointers, no matter if we are in debug-mode or not.

If we don't enable debug mode for libstdc++, a more performance friendly version (without the added bookkeeping) implementation will be used - and no diagnostics will be issued.





(Potentially) better Type Safety

Since the actual type of iterators are implementation-defined, this could be used to increase type-safety - but you will have to check the documentation of your implementation to see whether this is the case.


Consider the below example:

#include <vector>

struct A     { };
struct B : A { };

                                                      // .-- oops
// v
void it_func (std::vector<B>::iterator beg, std::vector<A>::iterator end);

void ptr_func (B * beg, A * end);
// ^-- oops

int
main (int argc, char *argv[])
{
std::vector<B> v1;

it_func (v1.begin (), v1.end ()); // (A)
ptr_func (v1.data (), v1.data () + v1.size ()); // (B)
}

Elaboration

  • (A) could, depending on the implementation, be a compile-time error since std::vector<A>::iterator and std::vector<B>::iterator potentially isn't of the same type.
  • (B) would, however, always compile since there's an implicit conversion from B* to A*.


Related Topics



Leave a reply



Submit