zoukankan      html  css  js  c++  java
  • 山寨STL实现之list

    在STL中list是以双向链表的方式来存储的,应此使用给定的下标值来找到对应的节点所需的时间复杂度为O(n),相比vector直接使用原生指针会慢一些。

    因为是双向链表的关系,那么必然有一种结构来表示链表中的节点。
            template <typename T>
            struct __list_node
            {
                __list_node<T>* prev;
                __list_node<T>* next;
                T data;

                __list_node() : prev(NULL), next(NULL)
                {
                }

                __list_node(const T& x) : prev(NULL), next(NULL), data(x)
                {
                }
            };

    然后我们定义出其iterator和const_iterator的结构
            template <typename T>
            struct __list_iterator
            {
                typedef __list_iterator<T> iterator;
                typedef T                  value_type;
                typedef ptrdiff_t          difference_type;
                typedef T*                 pointer;
                typedef T&                 reference;
                typedef const T*           const_pointer;
                typedef const T&           const_reference;
                typedef __list_node<T>*    link_type;
                typedef void*              void_pointer;

                link_type node;

                __list_iterator(link_type x) : node(x)
                {
                }

                __list_iterator(const __list_const_iterator<T>& x) : node(x.node)
                {
                }

                __list_iterator() : node(NULL)
                {
                }

                inline iterator& operator++()
                {
                    node = ((link_type)node)->next;
                    return *this;
                }

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

                inline iterator& operator--()
                {
                    node = ((link_type)node)->prev;
                    return *this;
                }

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

                inline reference operator*()const
                {
                    return node->data;
                }

                inline bool operator==(const iterator& x)const
                {
                    return node == x.node;
                }

                inline bool operator!=(const iterator& x)const
                {
                    return node != x.node;
                }
            };
    由于const_iterator与iterator的结构类似,这里不再贴出。其中重载了++与--运算符,实际上就是节点的前后移动。

    然后看一下list的定义
            template <typename T>
            class list
            {
            }

    让我们来看看list中的别名
            public:
                typedef T                        value_type;
                typedef T*                       pointer;
                typedef __list_iterator<T>       iterator;
                typedef __list_const_iterator<T> const_iterator;
                typedef T&                       reference;
                typedef const T&                 const_reference;
                typedef size_t                   size_type;
                typedef ptrdiff_t                difference_type;
                typedef reverse_iterator<const_iterator, value_type, size_type, difference_type> const_reverse_iterator;
                typedef reverse_iterator<iterator, value_type, size_type, difference_type> reverse_iterator;

    下面是其内部的成员变量
            protected:
                typedef __list_node<T>*           link_type;
                typedef list<T>                   self;
                typedef allocator<__list_node<T> > Node_Alloc;

                link_type node;
                size_type length;
    在STL中从begin到end总是以一个前闭后开的形式来表示的,应此我们给出一个node节点来表示end所指位置,而node节点的前驱则是这个list的起始节点,而length则存储了这个list的元素数量。

    下面来看看list中最基本的构造函数和析构函数
                list() : length(0)
                {
                    node = Node_Alloc::allocate();
                    node->next = node;
                    node->prev = node;
                }

                ~list()
                {
                    clear();
                    Node_Alloc::deallocate(node);
                }
    在list对象构造之初,首先构造出node节点,使其的前驱和后继都指向其本身,应此通过begin和end拿出的迭代器为同一个。在list对象析构时,首先将这个list清空,然后将构造出的node节点释放掉。

    然后是其begin和end方法,用来获取第一个元素和最后一个元素的后一个元素的迭代器
                inline iterator begin()
                {
                    return node->next;
                }

                inline const_iterator begin()const
                {
                    return node->next;
                }

                inline iterator end()
                {
                    return node;
                }

                inline const_iterator end()const
                {
                    return node;
                }

    front和back分别被用来获取第一个元素和最后一个元素
                inline reference front()
                {
                    return *begin();
                }

                inline const_reference front()const
                {
                    return *begin();
                }

                inline reference back()
                {
                    return *end();
                }

                inline const_reference back()const
                {
                    return *end();
                }

    empty、size分别被用来判别容器是否为空、获取容器内元素的个数
                inline bool empty()const
                {
                    return length == 0;
                }

                inline size_type size()const
                {
                    return length;
                }

    list与vector不同的是list是双向的,应此它允许从头尾两个方向来插入和删除元素
                inline void push_front(const T& x)
                {
                    insert(begin(), x);
                }

                inline void push_back(const T& x)
                {
                    insert(end(), x);
                }

                inline void pop_front()
                {
                    erase(begin());
                }

                inline void pop_back()
                {
                    erase(--end());
                }

    然后我们来看一下push的本质,insert函数
                iterator insert(const iterator& position, const T& x)
                {
                    if(!inRange(position)) throw "out of range";
                    link_type tmp = Node_Alloc::allocate();
                    construct(tmp, x);
                    tmp->prev = position.node->prev;
                    tmp->next = position.node;
                    position.node->prev->next = tmp;
                    position.node->prev = tmp;
                    ++length;
                    return tmp;
                }
    这里首先会检查这个迭代器是否属于这个list,然后构造出一个新节点,并把它插入到这个迭代器的前面,最后将节点数+1。

    然后是其删除节点函数erase
                void erase(const iterator& position)
                {
                    if(!inRange(position)) throw "out of range";
                    position.node->prev->next = position.node->next;
                    position.node->next->prev = position.node->prev;
                    destruct(&position.node->data, has_destruct(position.node->data));
                    Node_Alloc::deallocate(position.node);
                    --length;
                }
    这里同样会检查这个迭代器是否属于这个list,然后将这个节点移除,最后析构并释放内存空间。

    最后让我们来看一下list中重载的运算符
                self& operator=(const self& x)
                {
                    if(this == &x) return *this;

                    iterator first1 = begin();
                    iterator last1 = end();
                    const_iterator first2 = x.begin();
                    const_iterator last2 = x.end();
                    while (first1 != last1 && first2 != last2) *first1++ = *first2++;
                    if (first2 == last2) erase(first1, last1);
                    else insert(last1, first2, last2);
                    return *this;
                }

                reference operator[](size_type n)
                {
                    if(n < 0 || n >= length) throw "out of range";
                    link_type current = NULL;
                    if(n < length / 2)
                    {
                        current = node->next;
                        for(size_type i = 0; i < n; i++, current = current->next);
                    }
                    else
                    {
                        n = length - n - 1;
                        current = node->prev;
                        for(size_type i = 0; i < n; i++, current = current->prev);
                    }
                    return current->data;
                }

                inline value_type at(size_type n)
                {
                    return operator[](n);
                }
    因为其内部使用的是双向链表,应此通过指定下标来获取这个元素是可分别从两头进行移动指针。

    至此,list的讲解已完成,完整代码请到http://qlanguage.codeplex.com下载
  • 相关阅读:
    在Power BI报表和仪表板中显示刷新日期时间
    微软Power BI 每月功能更新系列——12月Power BI 新功能学习
    在Microsoft Power BI中创建地图的10种方法
    您应该将报表从Excel转换为Power BI的8个原因
    OBS录制全屏游戏的方法(超好录屏)
    关于Adobe Premiere Pro视音频不同步的解决方法
    Npcap:Nmap项目里一个为Windows而生的嗅探库 Npcap: Nmap Project's packet sniffing library for Windows
    关于被malloc分配内存的指针
    VS2017 高级使用方法
    Npcap环境配置(Winpcap后继者) pcap的一种
  • 原文地址:https://www.cnblogs.com/lwch/p/2630917.html
Copyright © 2011-2022 走看看