C++ source code analysis - list

  Foreword : I have read Mr. Hou's "STL Source Code Analysis" before, but that was many years ago. Now, sometimes you need to understand the implementation of STL used in actual work to check problems and crashes at work. Therefore, it is planned to go through the source code of STL again.
  Abstract : This article describes listthe implementation of libcxx in llvm.
  Keywords : list
  Other : Reference code LLVM-libcxx
  Note : The implementation of llvm when referring to the code is different from the implementation of gnu and msvc.

  listIt is a doubly linked list provided by STL, which can operate elements at the head and tail.
insert image description here

1 node

  As forward_listwith , lista distinction is made between indexes and nodes. Indexes only contain pointers to the corresponding access nodes, while nodes contain both linked pointers and data members.

template <class _Tp, class _VoidPtr>
struct __list_node_base{
    
    
    typedef __list_node_pointer_traits<_Tp, _VoidPtr> _NodeTraits;
    typedef typename _NodeTraits::__link_pointer __link_pointer;
    __link_pointer __prev_;         //前向指针
    __link_pointer __next_;         //后向指针
};

template <class _Tp, class _VoidPtr>
struct _LIBCPP_STANDALONE_DEBUG __list_node : public __list_node_base<_Tp, _VoidPtr>{
    
    
    _Tp __value_;
};

2 iterators

  listThe iterator is a list_nodepointer, and its self-increment and self-decrement operations are relatively simple to access the forward and backward connection pointers of the node.

template <class _Tp, class _VoidPtr>
class _LIBCPP_TEMPLATE_VIS __list_iterator{
    
    
    typedef __list_node_pointer_traits<_Tp, _VoidPtr> _NodeTraits;
    typedef typename _NodeTraits::__link_pointer __link_pointer;

    __link_pointer __ptr_;

    __list_iterator& operator++(){
    
    
        _LIBCPP_DEBUG_ASSERT(__get_const_db()->__dereferenceable(this),
                             "Attempted to increment a non-incrementable list::iterator");
        __ptr_ = __ptr_->__next_;
        return *this;
    }

    __list_iterator& operator--(){
    
    
        _LIBCPP_DEBUG_ASSERT(__get_const_db()->__decrementable(this),
                             "Attempted to decrement a non-decrementable list::iterator");
        __ptr_ = __ptr_->__prev_;
        return *this;
    }
};

3 listachieved

  listThe memory layout of is __list_impldescribed by , and the members __end_as index nodes describe the head and tail of the current linked list. __end_The nextand prevtwo pointers point to the head and tail nodes of the linked list respectively, __size_alloc_and the length of the current linked list is saved.

template <class _Tp, class _Alloc>
class __list_imp{
    
    
    __node_base __end_;
    __compressed_pair<size_type, __node_allocator> __size_alloc_;

    iterator begin() _NOEXCEPT{
    
    
        return iterator(__end_.__next_, this);
    }

    iterator end() _NOEXCEPT{
    
    
        return iterator(__end_as_link(), this);
    }
};

  The operation of the node is a typical double-linked list operation, there is nothing to say. clearThe operation for-loopis realized by traversing each node and destroying the corresponding node object and memory.

template <class _Tp, class _Alloc>
void __list_imp<_Tp, _Alloc>::clear() _NOEXCEPT{
    
    
    if (!empty()){
    
    
        __node_allocator& __na = __node_alloc();
        __link_pointer __f = __end_.__next_;
        __link_pointer __l = __end_as_link();
        __unlink_nodes(__f, __l->__prev_);
        __sz() = 0;
        while (__f != __l){
    
    
            __node_pointer __np = __f->__as_node();
            __f = __f->__next_;
            __node_alloc_traits::destroy(__na, _VSTD::addressof(__np->__value_));
            __node_alloc_traits::deallocate(__na, __np, 1);
        }
        std::__debug_db_invalidate_all(this);
    }
}

  listIt __list_implencapsulates common linked list operations on the basis of .

template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
class _LIBCPP_TEMPLATE_VIS list : private __list_imp<_Tp, _Alloc>{
    
    };

  Let's take a brief look push_backat pop_backhow it is implemented (in fact, it is a simple insertion of linked list nodes).
  push_backFirst create memory to construct the input object on the corresponding memory, and then insert the node to the end. Node insertion is also relatively simple, which is the general double-linked list node operation, which will not be described in detail.

template <class _Tp, class _Alloc>
void list<_Tp, _Alloc>::push_back(const value_type& __x){
    
    
    __node_allocator& __na = base::__node_alloc();
    __hold_pointer __hold = __allocate_node(__na);
    __node_alloc_traits::construct(__na, _VSTD::addressof(__hold->__value_), __x);
    __link_nodes_at_back(__hold.get()->__as_link(), __hold.get()->__as_link());
    ++base::__sz();
    __hold.release();
}

template <class _Tp, class _Alloc>
inline void list<_Tp, _Alloc>::__link_nodes_at_back(__link_pointer __f, __link_pointer __l){
    
    
    __l->__next_ = base::__end_as_link();
    __f->__prev_ = base::__end_.__prev_;
    __f->__prev_->__next_ = __f;
    base::__end_.__prev_ = __l;
}

  pop_frontThe realization of is also relatively simple and intuitive, and will not be described in detail.

template <class _Tp, class _Alloc>
void list<_Tp, _Alloc>::pop_front(){
    
    
    _LIBCPP_ASSERT(!empty(), "list::pop_front() called with empty list");
    __node_allocator& __na = base::__node_alloc();
    __link_pointer __n = base::__end_.__next_;
    base::__unlink_nodes(__n, __n);
    --base::__sz();
    __node_pointer __np = __n->__as_node();
    __node_alloc_traits::destroy(__na, _VSTD::addressof(__np->__value_));
    __node_alloc_traits::deallocate(__na, __np, 1);
}

template <class _Tp, class _Alloc>
inline void __list_imp<_Tp, _Alloc>::__unlink_nodes(__link_pointer __f, __link_pointer __l)_NOEXCEPT{
    
    
    __f->__prev_->__next_ = __l->__next_;
    __l->__next_->__prev_ = __f->__prev_;
}

Guess you like

Origin blog.csdn.net/GrayOnDream/article/details/129915889