STL Source Analysis-Iterator Concept and Trats Programming Techniques

Keywords: Programming

1. Iterator pattern: Provides a method for sequentially searching for elements contained in a container without exposing the internal expression of the polymer.

2. The central idea of STL is to separate data containers and algorithms and design them independently of each other. Finally, iterators should pin them together.

3. Iterators are pointers only. The most important programming task is to overload operators * and operators - >.

4. Iterator example:

    #include<iostream>  
    #include<algorithm>  
    using namespace std;  

    template<typename T>  
    struct ListItem{  //Linked list node data structure  
        ListItem() :m_next(nullptr){}//Default constructor  
        ListItem(T value, ListItem* p = nullptr) { m_value = value; m_next = p; } //Constructor  
        ListItem* Next() const { return m_next; }  //Returns the m_next pointer  
        T Value() const { return m_value; } //Return value  
        T m_value;  //Stored data  
        ListItem* m_next; //A pointer to the next ListItem  
    };  

    template<typename T>  
    class List{ //Linked list data structure  
    public:  
        List() :m_begin(nullptr), m_befend(nullptr), m_end(nullptr){} //Default constructor  
        void Push_back(T value){  //Insert elements from the end of the list  
            ListItem<T>* temp = new ListItem<T>(value, nullptr);  
            if (m_begin == nullptr){  
                m_begin = m_befend = temp;  
            }  
            else{  
                m_befend->m_next = temp;  
                m_befend = temp;  
            }  
        }  
        void Push_front(T value){  //Insert elements from the head of the list  
            ListItem<T>* temp = new ListItem<T>(value);  
            if (m_begin == nullptr){  
                m_begin = m_befend = temp;  
            }  
            else{  
                temp->m_next = m_begin;  
                m_begin = temp;  
            }  

        }  
        ListItem<T>* Begin() const { return m_begin; }  //Return the list header pointer  
        ListItem<T>* End() const { return m_end; }      //Returns the tail pointer of the list  
        void Print(ostream& os = cout) const{        //Print list elements  
            for (ListItem<T>* p = Begin(); p != End(); p = p->Next())  
                os << p->Value() << " ";  
            os << endl;  
        }  
    private:  
        ListItem<T>* m_begin; //A pointer to the head of the List  
        ListItem<T>* m_befend; //Pointer to the last element of List  
        ListItem<T>* m_end; //Pointer to the end of List  
        long m_size; //Length of List  
    };  
    // ListIter inherits the iterator provided by STL to ensure compliance with STL specifications  
    template<typename T>  
    class ListIter :public iterator<forward_iterator_tag, T>{  
    public:  
        ListIter(T* p = nullptr) :m_ptr(p){}   //Default constructor  
        T& operator*() const { return *m_ptr; }; //Dereference, dereference  
        T* operator->() const { return m_ptr; } //Member access, member access  
        ListIter& operator++(){ m_ptr = m_ptr->Next(); return *this; } //Pre++ operation, exposing the ext () of ListItem  
        ListIter operator++(int){ ListIter temp = *this; ++*this; return temp; }//Postposition ++ operation  
        bool operator==(const ListIter& i)const{ return m_ptr == i.m_ptr; }//Determine whether two ListIter s point to the same address  
        bool operator!=(const ListIter& i)const{ return m_ptr != i.m_ptr; }//Determine whether two ListIter s point to different addresses  
    private:  
        T* m_ptr;  //Keep a connection with the container  
    };  

    template<typename T>  //In this case, the value type is int, the iter type is ListItem < int >, and the operator== overloaded function must be written.  
    bool operator==(const ListItem<T>& item, const T& n){  
        return item.Value() == n;  
    }  

    //Template < typename T > // STL source profiling said to write operator!= overloaded function, but my side is not successful, need to write operator == overloaded function  
    //bool operator!=(const ListItem<T>& item, const T& n){  
    //  return item.Value() != n;  
    //}  

    int main(){  
        List<int> mylist;  
        for (int i = 0; i < 5; ++i){  
            mylist.Push_front(i);  
            mylist.Push_back(i + 2);  
        }  
        mylist.Print();  

        ListIter<ListItem<int>> begin(mylist.Begin()); //Exposed ListItem  
        ListIter<ListItem<int>> end(mylist.End()); //Exposed ListItem  
        ListIter<ListItem<int>> iter;  

        iter = find(begin, end, 1);//Find 3 from the list  
        if (iter == end)  
            cout << "not found" << endl;  
        else  
            cout << "found" << endl;     //Output found  

        iter = find(begin, end, 7);//Find 3 from the list  
        if (iter == end)  
            cout << "not found" << endl;  //Output not found  
        else  
            cout << "found" << endl;  

        system("pause");  
        return 0;  
    }  

The example above exposes the Next() of ListItem in the main function. If not for iterators, ListItem should be hidden, so the iterator's development is left to the designer of List, so that implementation details can be encapsulated and not seen by users, which is why each container has its own iterator.

Iterators fall into five categories:

Input Iterator: This iterator refers to objects that are not allowed to change outside. Read only

Output Iterator: Write only.

Forward Iterator: Read and write on the interval formed by this iterator.

Bidirectional Iterator: Bidirectional Iterator: Bidirectional Iterator.

Random Access Iterator: The first four iterators provide only part of the pointer arithmetic capability (the first three support operator++, the fourth plus operator -), and the fifth one covers all pointer arithmetic capabilities, including p+n, p-n, p[n], p1-p2, p1

    template<class I>  
    struct iterator_traits{  
        typedef typename I::value_type  value_type;  
    };  

The meaning of this so-called traits is that if I defines its own value_type, then through the role of traits, the extracted value_type is I::value_type.

    template<class I>  
    struct iterator_traits<I*>{  
        typedef I value_type; //Partial Version - Iterator is a native pointer  
    };  
    template<class I>  
    struct iterator_traits<const I*>{  
    typedef I value_type; //Partial version - When the iterator is pointer-to-const, the extracted type is I rather than const I  
    };  

    template<class I>  
    struct iterator_traits{  
        typedef typename I::iterator_category  iterator_category;  
        typedef typename I::value_type  value_type;  
        typedef typename I::difference_type difference_type;  
        typedef typename I::pointer pointer;  
        typedef typename I::reference reference;  
    };  

7. The corresponding categories of iterators:

1) value_type: value_type refers to the type of object that the iterator refers to.

2) difference_type: difference_type is used to represent the distance between two iterators.

3) reference_type: reference_type refers to a reference to the type of object that the iterator refers to.

4) pointer_type: pointer_type is the pointer to the object that the iterator refers to.

5) iterator_category: iterator_category refers to the type of iterator. There are five types of iterator.

// Five types for marking

    struct input_iterator_tag{};  
    struct output_iterator_tag{};  
    struct forward_iterator_tag:public input_iterator_tag{};  
    struct bidirectional_iterator_tag :forward_iterator_tag{};  
    struct random_access_iterator_tag : public bidirectional_iteratir_tag{};  

8. STL provides an iterator class from which each newly designed iterator inherits. This ensures that these custom iterators conform to the specifications required by STL. The iterator class is defined as follows:

template<typename Category,  
         typename T,  
         typename Distance = ptrdiff_t,  
         typename Pointer = T*,  
         typename Reference = T&>  
struct iterator  
{  
    typedef Category iterator_category;  
    typedef T value_type;  
    typedef Distance difference_type;  
    typedef Pointer pointer;  
    typedef Reference reference;  
}; 

Trats programming technique is widely used in STL implementation. It makes use of "embedded type" programming technique and compiler's parameter derivation function, and enhances the ability of type authentication which c++ can not provide.

Posted by ryankentp on Sun, 07 Jul 2019 15:47:00 -0700