STL source code analysis overview

Keywords: C++ cryptology STL

  catalogue

Node of list

  • list is more complex than vector's continuous linear space. Its storage space is discontinuous. The advantage is that each time an element is inserted and deleted, only one element's space needs to be configured or released
  • It is very convenient to insert and delete

Node of list

  • The list itself and the nodes of the list have different structures and need to be designed separately
  • The list node structure is a two-way linked list
template <class T>
struct __list_node{
    typedef void* void_pointer;
    void_pointer prev;   //The type of pointer is void *, which can be set to__ list_ node<T>
    void_pointer next;   
    T data;
};

list iterator

  • The list iterator cannot use ordinary pointers like the vector, because considering the discontinuity of memory allocation, the list iterator must be able to point to the node of the list and correctly perform operations such as increment, decrement, value (obtaining the data value of the member node), member access (obtaining the member of the node), etc
  • Because the iterator of list has the ability to move forward and backward, Bidirectional iterators are used
  • The insertion and splice operations of list will not invalidate the original list iterator, but not vector, because the vector insertion operation may cause memory reconfiguration and invalidate the original iterator
  • list delete element (erase) only the iterator pointing to the deleted element will be invalidated, and the other iterators will not be affected

  •   The list iterator is designed as follows
//Iterator design of list
template <class T,class Ref,class Ptr>
struct __list_iterator{
    typedef __list_iterator<T,T&,T*>    iterator;
    typedef __list_iterator<T,Ref,Ptr>  self;
    typedef std::bidirectional_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef __list_node<T>* link_type;
    typedef std::size_t size_type;
    typedef ptrdiff_t difference_type;

    link_type node;//An ordinary pointer is required inside the iterator to point to the list node

    //constructor
    __list_iterator(){}
    __list_iterator(link_type x):node(x){}
    __list_iterator(const iterator& x):node(x.node){}

    bool operator==(const self& x) const{return (node == x.node);}
    bool operator!=(const self& x) const{return (node != x.node);}

    //The following values of iterators are the data values of nodes
    reference operator*() const {return (*node).data;}
    //The following is the standard practice of member access operators for members of iterators
    pointer operator->() const {return &(operator*());}

    //Add a node to the iterator
    self& operator++(){
        node = (link_type)((*node).next);
    }
    
    self operator++(int){
        self tmp = *this;
        ++*this;
        return tmp;
    }
    
    //Subtract one node from the iterator and back one node
    self& operator--(){
        node = (link_type)((*node).prev);
    }
    
    self operator--(int){
        self tmp = *this;
        --*this;
        return tmp;
    }
};

list data structure

  • SGI list is not only a two-way linked list, but also a circular two-way linked list. It only needs a pointer to complete the traversal of the whole linked list
//Iterator design of list
template <class T,class Ref,class Ptr>
struct __list_iterator{
    typedef __list_iterator<T,T&,T*>    iterator;
    typedef __list_iterator<T,Ref,Ptr>  self;
    typedef std::bidirectional_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef __list_node<T>* link_type;
    typedef std::size_t size_type;
    typedef ptrdiff_t difference_type;

    link_type node;//An ordinary pointer is required inside the iterator to point to the list node

    //constructor
    __list_iterator(){}
    __list_iterator(link_type x):node(x){}
    __list_iterator(const iterator& x):node(x.node){}

    bool operator==(const self& x) const{return (node == x.node);}
    bool operator!=(const self& x) const{return (node != x.node);}

    //The following values of iterators are the data values of nodes
    reference operator*() const {return (*node).data;}
    //The following is the standard practice of member access operators for members of iterators
    pointer operator->() const {return &(operator*());}

    //Add a node to the iterator
    self& operator++(){
        node = (link_type)((*node).next);
    }

    self operator++(int){
        self tmp = *this;
        ++*this;
        return tmp;
    }

    //Subtract one node from the iterator and back one node
    self& operator--(){
        node = (link_type)((*node).prev);
    }

    self operator--(int){
        self tmp = *this;
        --*this;
        return tmp;
    }

    //Let the pointer node deliberately point to a blank node at the end, and the node node will meet the requirements of STL for the front opening and rear closing interval and become a list iterator
    iterator begin(){return (link_type)(*node->next);}
    iterator end(){return node;}
    bool empty()const{return node->next == node;}
    size_type size()const{
        size_type result = 0;
        std::distance(begin(),end(),result);
        return result;
    }
    //Get the content of the header node (element value)
    reference front(){
        return *begin();
    }
    //Take the content of the tail node (element value)
    reference back(){
        return *(--end());
    }
};
//List circular bidirectional linked list is designed as follows
template <class T,class Alloc> //alloc is used as the configurator by default
class list{
protected:
    typedef __list_node<T> list_node;
public:
    typedef list_node* link_type;
protected:
    link_type node; //With only one pointer, you can cycle through the whole ring-shaped two-way linked list
};

  list construction and memory management

  • See code Notes for details
//List circular bidirectional linked list is designed as follows
template <class T,class Alloc> //alloc is used as the configurator by default
class list{

protected:
    typedef __list_node<T> list_node;
    //The private space configurator configures the size of one node at a time
public:
    typedef list_node* link_type;
    typedef simple_alloc<list_node,Alloc>list_node_allocator;
    //list_node_allocator(n) means to allocate N node spaces. The following four functions are used to configure, release, construct and destroy a node respectively
protected:
    //Configure a node and return
    link_type get_node(){return list_node_allocator::allocate();}
    //Release a node
    link_type put_node(){return list_node_allocator::deallocate();}
    //Generate (configure and construct) a node with element values
    link_type create_node(const T& x){
        link_type p = get_node();
        Chy::allocator<T>::construct(p->data,x); //Basic tools for global function construction / deconstruction
        return p;
    }
    //Destroy (destruct and release) a node
    void destroy_node(link_type p){
        Chy::allocator<T>::destroy(&p->data);  //Basic tools for global function construction / deconstruction
    }

public:
    //Constructor
    //Generate an empty linked list
    list(){empty_initialize();} //Generate an empty linked list
protected:
    link_type node; //With only one pointer, you can cycle through the whole ring-shaped two-way linked list
    void empty_initialize(){
        node = get_node();  //Configure a node space so that node points to it
        node->next = node;  //Make the head and tail of the node point to yourself without setting the element value
        node->prev = node;  
    }
};

  •   When using push_ When back() inserts a new element into the end of the list, the function calls insert();

list element operation

 

//Clear all nodes (entire linked list)
template <class T,class Alloc>
void list<T,Alloc>::clear() {
    link_type cur = (link_type)node->next; //begin()
    while (cur != node){   //Traverse each node
        link_type tmp = cur;
        cur = (link_type)cur->next;
        destroy_node(tmp); //Destroy, destruct and release a node
    }
    //Restore the original state of node
    node->next = node;
    node->prev = node;
}

//Clear all elements with value
template<class T,class Alloc>
void list<T,Alloc>::remove(const T &value) {
    typedef typename __list_iterator<T,T&,T*>::iterator iterator;
    iterator first = __list_iterator<T,T&,T*>::begin();
    iterator last = __list_iterator<T,T&,T*>::end();
    while (first != last){
        iterator next = first;
        ++next;
        if (*first == value){
            __list_iterator<T,T&,T*>::erase(value);
            first = next;
        }
    }
}

//Remove continuous elements with the same value. Note: only "continuous and same elements" will be deleted. Only one element remains
template <class T,class Alloc>
void list<T,Alloc>::unique() {
    typedef typename __list_iterator<T,T&,T*>::iterator iterator;
    iterator first = __list_iterator<T,T&,T*>::begin();
    iterator last = __list_iterator<T,T&,T*>::end();
    if (first == last){
        return; //An empty linked list does nothing
    }
    iterator next = first;
    while(++next != last){       //Traverse each node
        if (*first == *next){    //If the same element exists in this section
            __list_iterator<T,T&,T*>::erase(next); //Remove
        } else{
            first = next;        //Adjust pointer
        }
        next = first;            //Correction section range
    }
}
  • List is a two-way circular linked list. It only needs to deal with boundary conditions. The operations of insertion and deletion at the head and tail are almost the same
  • Remove (erase) the element pointed to by an iterator, just move the pointer

  • list provides a transfer operation, which is technically a pointer movement between nodes before migrating elements of a continuous range to a specific location
  • The transfer migration operation is the basis of other operations (splice, sort, merge)
  • Source code of transfer  

  • The [first, last) interval received by transfer can be in the same list
  • transfer is not an open interface. List provides a so-called splice operation to move elements of a contact range from one list to a fixed point of another (or the same) list  
    //Move all elements in [first, last] before position
    void transfer(iterator position,iterator first,iterator last){
        if (last != position){
            (*(link_type((*last.node).prev))).next = position.node; //1
            (*(link_type((*first.node).prev))).next = last.node;    //2
            (*(link_type((*position.node).prev))).next = first.node;//3
            link_type tmp = link_type ((*position.node).prev);      //4
            (*position.node).prev = (*last.node).prev;              //5
            (*last.node).prev = (*first.node).prev;                 //6
            (*first.node).prev = tmp;                               //7
        }
    }

 

  • In essence, it is the pointer movement between nodes. The above split, reverse and sort functions themselves rely on the transfer function

 

  •   Complete code   Not necessarily
#include <iostream>
#include <list>

template<class T,class Alloc>
class simple_alloc{
public:
    static T* allocate(std::size_t n){
        return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));
    }
    static T* allocate(void){
        return (T*)Alloc::allocate(sizeof (T));
    }

    static void deallocate(T* p,size_t n){
        if (n!=0){
            Alloc::deallocate(p,n * sizeof(T));
        }
    }
    static void deallocate(T* p){
        Alloc::deallocate(p,sizeof(T));
    }
};


namespace Chy{
    template <class T>
    inline T* _allocate(ptrdiff_t size,T*){
        std::set_new_handler(0);
        T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));
        if (tmp == 0){
            std::cerr << "out of memory" << std::endl;
            exit(1);
        }
        return tmp;
    }

    template<class T>
    inline void _deallocate(T* buffer){
        ::operator delete (buffer);
    }

    template<class T1,class T2>
    inline void _construct(T1 *p,const T2& value){
        new(p) T1 (value);  //Do not understand
    }

    template <class T>
    inline void _destroy(T* ptr){
        ptr->~T();
    }

    template <class T>
    class allocator{
    public:
        typedef T           value_type;
        typedef T*          pointer;
        typedef const T*    const_pointer;
        typedef T&          reference;
        typedef const T&    const_reference;
        typedef std::size_t size_type;
        typedef ptrdiff_t   difference_type;

        template<class U>
        struct rebind{
            typedef allocator<U>other;
        };

        pointer allocate(size_type n,const void * hint = 0){
            return _allocate((difference_type)n,(pointer)0);
        }

        void deallocate(pointer p,size_type n){
            _deallocate(p);
        }

        void construct(pointer p,const T& value){
            _construct(p,value);
        }

        void destroy(pointer p){
            _destroy(p);
        }

        pointer address(reference x){
            return (pointer)&x;
        }

        const_pointer const_address(const_reference x){
            return (const_pointer)&x;
        }

        size_type max_size()const{
            return size_type(UINT_MAX/sizeof (T));
        }
    };
}

//If copy construction is equivalent to assignment and designer is trivial, the following will be valid
//If it is a POD type, the process will jump to the following function, which is obtained through the parameter derivation mechanism of function template
template<class ForwardIterator,class Size,class T>
inline ForwardIterator __uninitizlized_fill_n_aux(ForwardIterator first,Size n,const T&x){
    return fill_n(first,n,x); //Give it to higher-order functions for execution
}

struct __true_type{};
struct __false_type{};

template<class T>
struct __type_traits {
    typedef __true_type this_dummy_member_must_be_first;
    typedef __false_type has_trivial_default_constructor;
    typedef __false_type has_trivial_copy_constructor;
    typedef __false_type has_trivial_assignment_constructor;
    typedef __false_type has_trivial_destructor;
    typedef __false_type is_POD_type;
};

//The logic of the function is
//First extract the value type of iterator first, and then judge whether this type is POD type
template<class ForwardIterator,class Size,class T,class T1>
inline ForwardIterator __uninitizlized_fill_n(ForwardIterator first,Size n,const T&x,T1*){
    //The following uses _type _traits < T1 >:: is_pod _typeis _pod
    typedef typename __type_traits<T1>::is_POD_type is_POD;
    return __uninitizlized_fill_n_aux(first,n,x,is_POD());
}


template<class ForwardIterator,class Size,class T>
ForwardIterator uninitialized_fill_n(ForwardIterator first,Size n,const T&x){
    return __uninitizlized_fill_n(first,n,x,value_type(first));
    //Use value_type() to determine the first value type
}

//Node structure design of list
template <class T>
struct __list_node{
    typedef void* void_pointer;
    void_pointer prev;   //The type of pointer is void *, which can be set to _list_node < T >
    void_pointer next;
    T data;
};



//Iterator design of list
template <class T,class Ref,class Ptr>
struct __list_iterator{
    typedef __list_iterator<T,T&,T*>    iterator;
    typedef __list_iterator<T,Ref,Ptr>  self;
    typedef std::bidirectional_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef __list_node<T>* link_type;
    typedef std::size_t size_type;
    typedef ptrdiff_t difference_type;

    link_type node;//An ordinary pointer is required inside the iterator to point to the list node

    //constructor
    __list_iterator(){}
    __list_iterator(link_type x):node(x){}
    __list_iterator(const iterator& x):node(x.node){}

    bool operator==(const self& x) const{return (node == x.node);}
    bool operator!=(const self& x) const{return (node != x.node);}

    //The following values of iterators are the data values of nodes
    reference operator*() const {return (*node).data;}
    //The following is the standard practice of member access operators for members of iterators
    pointer operator->() const {return &(operator*());}

    //Add a node to the iterator
    self& operator++(){
        node = (link_type)((*node).next);
    }

    self operator++(int){
        self tmp = *this;
        ++*this;
        return tmp;
    }

    //Subtract one node from the iterator and back one node
    self& operator--(){
        node = (link_type)((*node).prev);
    }

    self operator--(int){
        self tmp = *this;
        --*this;
        return tmp;
    }

};

//List circular bidirectional linked list is designed as follows
template <class T,class Alloc> //alloc is used as the configurator by default
class list{

protected:
    typedef typename __list_iterator<T,T&,T*>::iterator iterator;
    typedef typename __list_iterator<T,T&,T*>::value_type value_type;
    typedef typename __list_iterator<T,T&,T*>::reference reference;
    typedef typename __list_iterator<T,T&,T*>::size_type size_type;

    typedef __list_node<T> list_node;
    //The private space configurator configures the size of one node at a time
public:
    typedef list_node* link_type;
    typedef simple_alloc<list_node,Alloc>list_node_allocator;
    //list_node_allocator(n) means to allocate N node spaces. The following four functions are used to configure, release, construct and destroy a node respectively
protected:
    //Configure a node and return
    link_type get_node(){return list_node_allocator::allocate();}
    //Release a node
    link_type put_node(){return list_node_allocator::deallocate();}
    //Generate (configure and construct) a node with element values
    link_type create_node(const T& x){
        link_type p = get_node();
        Chy::allocator<T>::construct(p->data,x); //Basic tools for global function construction / deconstruction
        return p;
    }
    //Destroy (destruct and release) a node
    void destroy_node(link_type p){
        Chy::allocator<T>::destroy(&p->data);  //Basic tools for global function construction / deconstruction
    }

public:
    //Let the pointer node deliberately point to a blank node at the end, and the node node will meet the requirements of STL for the front opening and rear closing interval and become a list iterator
    iterator begin(){return (link_type)(*node->next);}
    iterator end(){return node;}
    bool empty()const{return node->next == node;}
    size_type size()const{
        size_type result = 0;
        std::distance(begin(),end(),result);
        return result;
    }
    //Get the content of the header node (element value)
    reference front(){
        return *begin();
    }
    //Take the content of the tail node (element value)
    reference back(){
        return *(--end());
    }

    //Insert element for push_back() function call
    //The insert function needs to configure and construct a node, and then perform pointer operation at the tail end to insert the new node
    //The purpose of the function is to insert a node with the content of x at the position specified by the iterator
    iterator insert(iterator position,const T&x){ //Generate a node. The default initialization element value is x
        link_type tmp = create_node(x);
        //Adjust the double pointer to insert tmp
        tmp->next = position.node;
        tmp->prev = position.node->prev;
        (link_type(position.node->prev))->next = tmp;
        position.node->prev = tmp;
        return tmp;
    }

    //Insert a node as the head node
    void push_front(const T& x){
        insert(begin(),x);
    }

    //Insert a node as the tail node
    void push_back(const T& x){
        insert(end(),x);
    }

    //Remove the node pointed to by the iterator position
    iterator erase(iterator position){
        link_type next_node = link_type (position.node->next);
        link_type prev_node = link_type (position.node->prev);
        prev_node->next = next_node;
        next_node->prev = prev_node;
        destroy_node(position.node);
        return iterator (next_node);
    }

    //Remove head node
    void pop_front(){
        erase(begin());
    }

    //Remove tail node
    void pop_back(){
        iterator tmp = end();
        erase(--tmp);
    }

    //Move all elements in [first, last] before position
    void transfer(iterator position,iterator first,iterator last){
        if (last != position){
            (*(link_type((*last.node).prev))).next = position.node; //1
            (*(link_type((*first.node).prev))).next = last.node;    //2
            (*(link_type((*position.node).prev))).next = first.node;//3
            link_type tmp = link_type ((*position.node).prev);      //4
            (*position.node).prev = (*last.node).prev;              //5
            (*last.node).prev = (*first.node).prev;                 //6
            (*first.node).prev = tmp;                               //7
        }
    }





public:
    //Constructor
    //Generate an empty linked list
    list(){empty_initialize();} //Generate an empty linked list

    void clear();
    void remove(const T& value);
    void unique();


protected:
    link_type node; //With only one pointer, you can cycle through the whole ring-shaped two-way linked list

    //Null initialization
    void empty_initialize(){
        node = get_node();  //Configure a node space so that node points to it
        node->next = node;  //Make the head and tail of the node point to yourself without setting the element value
        node->prev = node;
    }

public:
    //X must be different from * this before combining x to the position specified
    void splice(__list_iterator<T, T &, T *>, list &x);

    //Combine the element i points to before the position specified by position. Position and i can point to the same list
    void splice(iterator position,list& ,iterator i){
        iterator j = i;
        ++j;
        if (position == i || position == j){
            return;
        }
        transfer(position,i,j);
    }

    //Join the elements in [first, last] before the position specified by position
    //position and [first, last) may point to the same list
    //However, position cannot be inside [first, last]
    void splice(iterator position,list &,iterator first,iterator last){
        if (first != last){
            transfer(position,first,last);
        }
    }

    //Merge () merges x into * this, but the contents of both lists must be sorted incrementally first
    void meage(list& x){
        iterator first1 = begin();
        iterator last1  = end();
        iterator first2 = x.begin();
        iterator last2 = x.end();
        //The two list implementations need to be sorted
        while (first1 != last1 && first2 != last2){
            if (*first2 < *first1){
                iterator next = first2;
                transfer(first1,first2,++next);
                first2 = next;
            } else{
                ++first1;
            }
            if (first2 != last2){
                transfer(last1,first2,last2);
            }
        }
    }

    //reverse() reversely resets the contents of this
    void reverse(){
        //The following judgment, if it is an empty linked list, or there is only one element, no operation will be performed
        //Use size() = = 0 | size() = = 1 to judge, which is slow
        if (node->next == node ||link_type(node->next)->next == node)
            return;
        iterator first = begin();
        ++first;
        while (first != end()){
            iterator old = first;
            ++first;
            transfer(begin(),old,first);
        }
    }

    //list cannot use sort() of the STL algorithm because the STL algorithm only accepts random access iterator
    //This function uses quick sort
    void sort(){
        //The following judgment, if it is an empty linked list, or there is only one element, no operation will be performed
        //Use size() = = 0 | size() = = 1 to judge, which is slow
        if (node->next == node ||link_type(node->next)->next == node)
            return;
        //Use some new lists as the mediation data store
        list carry;
        list counter[64];
        int fill = 0;
        while (!empty()){
            carry.splice(carry.begin(),*this,begin());
            int i = 0;
            while (i < fill && !counter[i].empty()){
                counter[i].merage(carry);
                carry.swap(counter[i++]);
            }
            carry.swap(counter[i]);
            if (i == fill){
                ++fill;
            }
        }
        for (int i = 1; i < fill; ++i) {
            counter[i].merage(counter[i-1]);
        }
        std::swap(counter[fill-1]);
    }
};


//Clear all nodes (entire linked list)
template <class T,class Alloc>
void list<T,Alloc>::clear() {
    link_type cur = (link_type)node->next; //begin()
    while (cur != node){   //Traverse each node
        link_type tmp = cur;
        cur = (link_type)cur->next;
        destroy_node(tmp); //Destroy, destruct and release a node
    }
    //Restore the original state of node
    node->next = node;
    node->prev = node;
}

//Clear all elements with value
template<class T,class Alloc>
void list<T,Alloc>::remove(const T &value) {
    typedef typename __list_iterator<T,T&,T*>::iterator iterator;
    iterator first = __list_iterator<T,T&,T*>::begin();
    iterator last = __list_iterator<T,T&,T*>::end();
    while (first != last){
        iterator next = first;
        ++next;
        if (*first == value){
            __list_iterator<T,T&,T*>::erase(value);
            first = next;
        }
    }
}

//Remove continuous elements with the same value. Note: only "continuous and same elements" will be deleted. Only one element remains
template <class T,class Alloc>
void list<T,Alloc>::unique() {
    typedef typename __list_iterator<T,T&,T*>::iterator iterator;
    iterator first = __list_iterator<T,T&,T*>::begin();
    iterator last = __list_iterator<T,T&,T*>::end();
    if (first == last){
        return; //An empty linked list does nothing
    }
    iterator next = first;
    while(++next != last){       //Traverse each node
        if (*first == *next){    //If the same element exists in this section
            __list_iterator<T,T&,T*>::erase(next); //Remove
        } else{
            first = next;        //Adjust pointer
        }
        next = first;            //Correction section range
    }
}

template <class T,class Alloc>
void list<T,Alloc>::splice(__list_iterator<T, T &, T *>position, list<T, Alloc> &x) {
    if (!x.empty()){
        transfer(position,x.begin(),x.end());
    }
}
  • The code understanding of transfer related functions is not very thorough

Posted by cesar110 on Fri, 19 Nov 2021 03:56:43 -0800