STL source code analysis (six) iterator

STL source code analysis (six) iterator


This article will take you to learn what is an iterator, and STL iterators specification

First, what is the iterator?

Iterator is a kind of pointer behavior (->, *, ++) things, it may be 原生指针, it can be overloaded operator- ``> operator * operator ++ 等运算符的objects'

For native pointer very clear, because the function of the pointer itself with

For iterator is an object, you may be a bit unclear, do not worry, here I use a very simple example, to tell you

Second, the realization of a simple container

First, we realized very, very simple container (a container object store data)

template <class T>
class MVector
{
public:
    MVector()
    {
        mStart = (T*)malloc(1024*1024);
        mEnd = mStart;
    }

    ~MVector()
    {
        free(mStart);
    }

    void push(const T& value)
    {
        *mEnd = value;
        ++mEnd;
    }

private:
    T* mStart;
    T* mEnd;
};

This enables a very simple container, the container can be seen inside an array, mStartthe beginning point to an array of mEndpoints to the next position of the last element in the array, this container only one way, that it is by pushadding elements

This container can be used with the following code

#include <iostream>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    MVector<int> mVt;

    for(int i = 0; i < 10; ++i)
        mVt.push(i);

	return 0;
}

The above-described procedure is simply to add data to the vessel

Think about, if we are to traverse all the elements in the container, how we need to do?

For this container, we can of course by some means, acquisition mStartand mEndpointer, then all of the array elements to traverse through them.

But in some of the more sophisticated container (or the bottom of the list is a red-black tree), in these containers, we can not simply pointers to iterate through all the elements, but we wanted to come by and access pointer in the same way container element, this time to design iterator objects, and reload the operator, the operation of the complex package, and provide the same function pointer

So in STL, most containers provide their corresponding iterator, here we come to design an iterator for the container

Third, to achieve the iterator container

First explain, for the above this container, in fact, it is as long as the iterator T*native pointer on the line, but in order to demonstrate how complex iterators is designed, there will be an object that implements the iterator, as follows

template <class T>
class Iterator
{
public:
    Iterator(T* data) : mData(data)
    {}

    Iterator& operator++()
    {
        ++mData;
        return *this;
    }

    T operator*()
    {
        return *mData;
    }

    bool operator!=(const Iterator& it)
    {
        return mData != it.mData;
    }

private:
    T* mData;
};

This iterator contains a pointer to the object, and then overloaded operator++, operator*, operator!=operator, so that it has a pointer to the sub-part nature

Once you have an iterator, we redesigned our vessels, as follows

template <class T>
class MVector
{
public:
    /* add */
    typedef Iterator<T> iterator;

    MVector()
    {
        mStart = (T*)malloc(1024*1024);
        mEnd = mStart;
    }

    ~MVector()
    {
        free(mStart);
    }

    void push(const T& value)
    {
        *mEnd = value;
        ++mEnd;
    }

    /* add */
    iterator begin()
    {
        return iterator(mStart);
    }

    /* add */
    iterator end()
    {
        return iterator(mEnd);
    }s
    
private:
    T* mStart;
    T* mEnd;
};

We use typedef Iterator<T> iteratoroutside provides iterator type of the container, and then provide begin()and end()to get end to end iterator

With these, we can traverse the elements of the container, as follows

int main(int argc, char* argv[])
{
    MVector<int> mVt;

    /* 添加元素 */
    for(int i = 0; i < 10; ++i)
        mVt.push(i);

    /* 遍历容器 */
    for(MVector<int>::iterator it = mVt.begin(); it != mVt.end(); ++it)
        std::cout<<*it<<" "; //访问元素
    
    std::cout<<std::endl;

    return 0;
}

The complete code

#include <iostream>
#include <stdlib.h>

template <class T>
class Iterator
{
public:
    Iterator(T* data) : mData(data)
    {}

    Iterator& operator++()
    {
        ++mData;
        return *this;
    }

    T operator*()
    {
        return *mData;
    }

    bool operator!=(const Iterator& it)
    {
        return mData != it.mData;
    }

private:
    T* mData;
};

template <class T>
class MVector
{
public:
    typedef Iterator<T> iterator;

    MVector()
    {
        mStart = (T*)malloc(1024*1024);
        mEnd = mStart;
    }

    ~MVector()
    {
        free(mStart);
    }

    iterator begin()
    {
        return iterator(mStart);
    }

    iterator end()
    {
        return iterator(mEnd);
    }

    void push(const T& value)
    {
        *mEnd = value;
        ++mEnd;
    }

private:
    T* mStart;
    T* mEnd;
};

int main(int argc, char* argv[])
{
    MVector<int> mVt;

    for(int i = 0; i < 10; ++i)
        mVt.push(i);

    /* 遍历容器 */
    for(MVector<int>::iterator it = mVt.begin(); it != mVt.end(); ++it)
        std::cout<<*it<<" ";
    std::cout<<std::endl;

    return 0;
}

Four, STL iterators specifications

After these talks, presumably you already know what an iterator, but we realize the above-mentioned STL iterators do not meet specifications, so for many of the STL algorithms, and we can not use, let's take a look at the STL iterators design specification

In the stl_iterator.hfile, define many things associated with the iterator

4.1 STL iterators specifications

First we look at, STL iterators requirements, STL iterators requirements need to be able to provide at least the above-mentioned five types, as follows

template <class Category, class T, class Distance = ptrdiff_t,
          class Pointer = T*, class Reference = T&>
struct iterator {
  typedef Category  iterator_category; //迭代器类型
  typedef T         value_type; //迭代器所指的类型
  typedef Distance  difference_type; //两个迭代器之间的距离描述
  typedef Pointer   pointer; //迭代器所指的类型的指针类型
  typedef Reference reference; //迭代器所指类型的引用类型
};

Why do I need this information?

Because the STL algorithms usually need this information to define the appropriate type, or to take different measures according to this information, if you design iterator does not meet these requirements, then you are in the use of STL algorithms, very may fail to compile

For other fields, not discussed here, and a good discussion below iterator categoriesiterator_category

4.2 STL iterators classification of

STL iterator divided into five categories, which are defined as follows

/* 输入式迭代器 */
struct input_iterator_tag {};

/* 输出式迭代器 */
struct output_iterator_tag {};

/* 单向迭代器 */
struct forward_iterator_tag : public input_iterator_tag {};

/* 双向迭代器 */
struct bidirectional_iterator_tag : public forward_iterator_tag {};

/* 随机访问迭代器 */
struct random_access_iterator_tag : public bidirectional_iterator_tag {};

Their relationship is as follows

Here Insert Picture Description

Iterator way: indicates a direction to move frame by frame (one-way linked list)

Bidirectional iterator: a rear direction of forward movement of one grid (doubly linked lists)

Random access iterators: can randomly access (array)

Why do we need iterator categories?

The main purpose is to allow the algorithm can use different strategies, Here is an example

In stl algorithms, there is one called advancealgorithms, whose role is directed to the mobile iterator defined below

inline void advance(InputIterator& i, Distance n)

Imagine now if the bottom of the iterator corresponding container is an array, then move 5 squares, only +5 on the line, if it is a linked list, then you need a frame by frame movement, it is clear that both the efficiency difference is quite big

STL implementation as follows

template <class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n) {
  __advance(i, n, iterator_category(i));
}

According iterator iterator_categorycall the appropriate __advancefunction

For iterator (random_access_iterator_tag) of random access

template <class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator& i, Distance n, 
                      random_access_iterator_tag) {
  i += n;
}

For bidirectional iterators

template <class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator& i, Distance n, 
                      bidirectional_iterator_tag) {
  if (n >= 0)
    while (n--) ++i;
  else
    while (n++) --i;
}

For ordinary iterators

template <class InputIterator, class Distance>
inline void __advance(InputIterator& i, Distance n, input_iterator_tag) {
  while (n--) ++i;
}

This article will be over

Published 107 original articles · won 197 Like · views 80000 +

Guess you like

Origin blog.csdn.net/weixin_42462202/article/details/101311046