【C++学习手札】模拟实现list

简介: 【C++学习手札】模拟实现list

一、list实际的底层原理

       C++的STL库中的list是使用带头的双向循环链表实现的。list中存储的每个元素包含了两个指针,一个指向前一个节点,一个指向后一个节点,这样就可以方便地在list中进行插入和删除操作,这些操作的时间复杂度都是O(1)。


        大致的结构如下:

        如多大家对于这个数据结构不太熟悉的话,不妨看看作者之前写的一篇关于带头双向循环链表的文章🌀【数据结构】—C语言实现双向链表(超详细!) 


二、list的模拟实现

       写在前面

       list的底层虽然是带头双向循环链表,但是对于它的实际实现不只是简单的链表而已,当然了,我们肯定会有链表的数据结构。但是,这个“结构”,经过了三层的封装才得以实现,分别是: 第一层:list的节点类

               第二层:list的迭代器类

               第三层:list类

本文目前只实现正向的迭代器,反向迭代器会在后面的文章统一vector、string、list一起实现。

各层封装的实现

       节点类

       主要是包括:对于两个双向指针的定义以及数据的定义,再是通过初始化列表对于节点的初始化。

    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())//通过初始化列表初始化值
            :_val(val)
            , _pPre(nullptr)
            , _pNext(nullptr)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

    迭代器类

        迭代器实际上是对于指针进行操作,因此我们实例化并且重新命名了节点类的指针PNode,由于迭代器分为是否常量正向迭代器,对此我们额外定义了两个模板参数Ref、Ptr用于控制其中重载运算符 T& operator*() 和 T* operator->()。后面的list类中会有体现。在迭代器中,其中,operator*和operator->返回指向节点的指针,operator++和operator--实现前后缀++/--运算符,operator==和operator!=用来比较两个迭代器是否指向同一个节点。

    //List的迭代器类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}
        ListIterator(const Self& l)
            :_pNode(l._pNode)
        {}
        T& operator*()
        {
            return _pNode->_val;
        }
        T* operator->()
        {
            return &*this;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPre;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return !(*this != l);
        }
    private:
        PNode _pNode;
    };

        list类

       这里先给出主要的封装,具体的实现后面详解。可以看到我们又对于节点类进行了实例化并且重新命名,并且定义了一个数据变量下面是重点了我们对于迭代器类也进行了实例化并且重新命名,特别是对于上面我们所提到的Ref和Ptr是有做变动的注意看对于是否常量正向迭代器分别做出了如下定义:T&, T*和const T&, const T*。这里所这的一些变动也是为了后续简化代码,避免我们因为动静态多一份代码。

请结合上面我们迭代器类中我们所提到的operator==和operator!=理解。
class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;//普通迭代器重命名
        typedef ListIterator<T, const T&, const T*> const_iterator;//静态迭代器重命名
    public:
    //...
    private:
        PNode _pHead;
    };

list类详解

       在C++中我们通常会进行各类函数的复用,以减少代码量和增加可读性。对此,我们尽量做到复用。

        迭代器实现

       返回头以及尾的迭代器,注意区分动静态 。

        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin()const
        {
            return const_iterator(_pHead->_pNext);
        }
        const_iterator end()const
        {
            return const_iterator(_pHead);
        }

list的修改操作

       Insert

 实现在pos位置前插入值为val的节点,开辟空间->保存原位置节点->新节点的前指针指向原节点的前一个节点->后节点指向原节点->该边原节点的前一个节点的后指针指向,指向新节点->原节点的前指针指向新节点->返回性节点的迭代器。

// 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode pNewNode = new Node(val);
            PNode pCur = pos._pNode;
            pNewNode->_pPre = pCur->_pPre;
            pNewNode->_pNext = pCur;
            pNewNode->_pPre->_pNext = pNewNode;
            pCur->_pPre = pNewNode;
            return iterator(pNewNode);
        }

erase

        删除pos位置的节点,返回该节点的下一个位置,保存删除j节点,保存原节点的下一个节点(用于返回)->一些列删除解链接操作->返回原节点的下一个节点的迭代器

        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            PNode pDel = pos._pNode;
            PNode pRet = pDel->_pNext;
            pDel->_pPre->_pNext = pDel->_pNext;
            pDel->_pNext->_pPre = pDel->_pPre;
            delete pDel;
            return iterator(pRet);
        }

swap

        void swap(list<T>& l)
        {
            pNode tmp = _pHead;
            _pHead = l._pHead;
            l._pHead = tmp;
        }

  复用操作(push_back/front、pop_back/front、clear)

        void push_back(const T& val)
        { 
            insert(end(), val);
        }
        void pop_back()
        { 
            erase(--end()); 
        }
        void push_front(const T& val) 
        { 
            insert(begin(), val); 
        }
        void pop_front() 
        { 
            erase(begin());
        }
        void clear()
        {
            iterator p = begin();
            while (p != end())
            {
                p = erase(p);
            }
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }

list的构造

建立头节点

       因为我们在构造、 拷贝等很多的场景中都会用到对于头结点的初始化,对此额外写一个函数用于减少代码量。

        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }

构造函数

       实现无参、含参初始化、迭代器构造。

        // List的构造
        list()
        {
            CreateHead();
        }
        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i = 0; i < n; ++i)
            {
                push_back(value);
           }
        }
        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            CreateHead();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

 拷贝构造‘=’重载

        list(const list<T>& l)
        {
            CreateHead();
            list<T> temp(l.begin(), l.end());
            this->swap(temp);
        }
        list<T>& operator=(const list<T> l)
        {
            this->swap(l);
            return *this;
        }

析构函数

        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }

list的空间管理


        size_t size()const
        {
            size_t size = 0;
            ListNode* p = _pHead->_pNext;
            while (p != _pHead)
            {
                size++;
                p = p->_pNext;
            }
            return size;
        }
        bool empty()const
        {
            return size() == 0;
        }

list的访问相关

       主要还是要区分是否为动静态相关。

        T& front()
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        const T& front()const
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }
        const T& back()const
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }

三、整体代码

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace lt
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())//通过初始化列表初始化值
            :_val(val)
            , _pPre(nullptr)
            , _pNext(nullptr)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };
    //List的迭代器类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}
        ListIterator(const Self& l)
            :_pNode(l._pNode)
        {}
        T& operator*()
        {
            return _pNode->_val;
        }
        T* operator->()
        {
            return &*this;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPre;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return !(*this != l);
        }
    private:
        PNode _pNode;
    };
    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;//普通迭代器重命名
        typedef ListIterator<T, const T&, const T*> const_iterator;//静态迭代器重命名
    public:
        ///
        // List的构造
        list()
        {
            CreateHead();
        }
        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i = 0; i < n; ++i)
            {
                push_back(value);
           }
        }
        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            CreateHead();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        list(const list<T>& l)
        {
            CreateHead();
            list<T> temp(l.begin(), l.end());
            this->swap(temp);
        }
        list<T>& operator=(const list<T> l)
        {
            this->swap(l);
            return *this;
        }
        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }
        ///
        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin()const
        {
            return const_iterator(_pHead->_pNext);
        }
        const_iterator end()const
        {
            return const_iterator(_pHead);
        }
        ///
        // List Capacity
        size_t size()const
        {
            size_t size = 0;
            ListNode* p = _pHead->_pNext;
            while (p != _pHead)
            {
                size++;
                p = p->_pNext;
            }
            return size;
        }
        bool empty()const
        {
            return size() == 0;
        }
        // List Access
        T& front()
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        const T& front()const
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }
        const T& back()const
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }
        // List Modify
        void push_back(const T& val)
        { 
            insert(end(), val);
        }
        void pop_back()
        { 
            erase(--end()); 
        }
        void push_front(const T& val) 
        { 
            insert(begin(), val); 
        }
        void pop_front() 
        { 
            erase(begin());
        }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode pNewNode = new Node(val);
            PNode pCur = pos._pNode;
            pNewNode->_pPre = pCur->_pPre;
            pNewNode->_pNext = pCur;
            pNewNode->_pPre->_pNext = pNewNode;
            pCur->_pPre = pNewNode;
            return iterator(pNewNode);
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            PNode pDel = pos._pNode;
            PNode pRet = pDel->_pNext;
            pDel->_pPre->_pNext = pDel->_pNext;
            pDel->_pNext->_pPre = pDel->_pPre;
            delete pDel;
            return iterator(pRet);
        }
        void clear()
        {
            iterator p = begin();
            while (p != end())
            {
                p = erase(p);
            }
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        void swap(list<T>& l)
        {
            pNode tmp = _pHead;
            _pHead = l._pHead;
            l._pHead = tmp;
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        PNode _pHead;
    };
};

    感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

相关文章
|
10月前
|
存储 Java 索引
(Python基础)新时代语言!一起学习Python吧!(二):字符编码由来;Python字符串、字符串格式化;list集合和tuple元组区别
字符编码 我们要清楚,计算机最开始的表达都是由二进制而来 我们要想通过二进制来表示我们熟知的字符看看以下的变化 例如: 1 的二进制编码为 0000 0001 我们通过A这个字符,让其在计算机内部存储(现如今,A 字符在地址通常表示为65) 现在拿A举例: 在计算机内部 A字符,它本身表示为 65这个数,在计算机底层会转为二进制码 也意味着A字符在底层表示为 1000001 通过这样的字符表示进行转换,逐步发展为拥有127个字符的编码存储到计算机中,这个编码表也被称为ASCII编码。 但随时代变迁,ASCII编码逐渐暴露短板,全球有上百种语言,光是ASCII编码并不能够满足需求
394 4
|
算法 C++ 容器
模拟实现c++中的list模版
模拟实现c++中的list模版
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
466 4
2023/11/10学习记录-C/C++对称分组加密DES
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
271 16
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
390 9
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
573 7
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
407 2
|
存储 编译器 C++
C++ initializer_list&&类型推导
在 C++ 中,`initializer_list` 提供了一种方便的方式来初始化容器和传递参数,而右值引用则是实现高效资源管理和移动语义的关键特性。尽管在实际应用中 `initializer_list&&` 并不常见,但理解其类型推导和使用方式有助于深入掌握现代 C++ 的高级特性。
284 4
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
676 6
|
存储 算法 C++
【C++打怪之路Lv10】-- list
【C++打怪之路Lv10】-- list
273 1