C++:list模拟实现

news2024/11/25 2:20:31

hello,各位小伙伴,本篇文章跟大家一起学习《C++:list模拟实现》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !
如果本篇文章对你有帮助,还请各位点点赞!!!
在这里插入图片描述
话不多说,开始进入正题

🍁list的逻辑结构以及节点代码

在这里插入图片描述

是一个双指针带头链表,所以我选择用一个结构体ListNode来维护节点,如下:

// 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;// 该结点的值
};

我对ListNode<T>改一个名字:Node

typedef ListNode<T> Node;
typedef Node* PNode;

🍁list类

🍃私有成员变量_pHead和私有成员函数CreateHead()

private:
    void CreateHead()// 创建头节点并且初始化
    {
        _pHead = new Node();
        _pHead->_pNext = _pHead;
        _pHead->_pPre = _pHead;
    }

    PNode _pHead;

🍃尾插函数和插入函数

尾插只是插入的其中一种方式,所以实现了插入函数,就能够实现尾插函数。
插入思路图解:在pos位置前插入值为val的节点

创建新节点值为value后;
使prev节点的_pNext指针指向newnode,newnode的节点的_pPre指向prev;
使cur节点的_pPre指针指向newnode,newnode的节点的_pNext指向cur;
最后返回iterator(newnode);

在这里插入图片描述

itearator为迭代器,后面会实现

  • 插入
// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
    Node* cur = pos._pNode;
    Node* newnode = new Node(val);
    Node* prev = cur->_pPre;

    // prev  newnode  cur
    prev->_pNext = newnode;
    newnode->_pPre = prev;
    newnode->_pNext = cur;
    cur->_pPre = newnode;
    
    return iterator(newnode);
}
  • 尾插
void push_back(const T& val)
{
	insert(end(), val); 
}

🍃构造函数

  • 无参构造
list(const PNode& pHead = nullptr)
{
    CreateHead();
    /*_pHead = new Node();
    _pHead->_pNext = _pHead;
    _pHead->_pPre = _pHead;*/
}
  • 带参构造(数值)
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.cbegin(), l.cend());

	// 与*this的头节点pHead交换指向
    swap(temp);
}

🍃析构函数

clear()为其中的成员函数,功能:清理list中的数据

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

    /*Node* cur = _pHead->_pNext;
    Node* tmp = cur->_pNext;
    while (cur != _pHead)
    {
        delete cur;
        cur = tmp;
        tmp = tmp->_pNext;
    }
    tmp = cur = nullptr;
    _pHead->_pNext = _pHead;
    _pHead->_pPre = _pHead;*/
}

🍃迭代器模拟

逻辑上并不难,也许难理解于模板

//List的迭代器结构体
template<class T, class Ref, class Ptr>
struct ListIterator
{
    typedef ListNode<T>* PNode;
    typedef ListIterator<T, Ref, Ptr> Self;

    ListIterator(PNode pNode = nullptr)
        :_pNode(pNode)
    {}

    ListIterator(const Self& l)
    {
        _pNode = l._pNode;
    }

    T& operator*()
    {
        assert(_pNode != _pNode->_pNext);
        return _pNode->_val;
    }

    T* operator->()
    {
        return &(*this);
    }

    Self& operator++()
    {
        _pNode = _pNode->_pNext;
        return *this;
    }

    Self operator++(int)
    {
        PNode* tmp = _pNode;
        _pNode = _pNode->_pNext;
        return tmp;
    }

    Self& operator--()
    {
        _pNode = _pNode->_pPre;
        return *this;
    }

    Self& operator--(int)
    {
        PNode* tmp = _pNode;
        _pNode = _pNode->_pPre;
        return tmp;
    }

    bool operator!=(const Self& l)
    {
        return _pNode != l._pNode;
    }

    bool operator==(const Self& l)
    {
        return !(*this != l);
    }
    PNode _pNode;
};

这段代码定义了一个模板结构 ListIterator,用于表示List类的迭代器。让我们来解释模板声明部分:

template<class T, class Ref, class Ptr>;

这一行是模板声明,定义了一个模板类 ListIterator,它有三个模板参数:T、Ref 和 Ptr。让我们逐个解释这些参数的作用:

1.T: 这是一个模板参数,表示迭代器指向的元素类型。在使用 ListIterator 时,你需要提供实际的类型作为 T 的值。
2.Ref: 这也是一个模板参数,表示迭代器的引用类型。通常情况下,当你通过迭代器解引用(使用 * 运算符)时,你希望得到的是元素的引用类型。所以 Ref 通常被设定为 T&,表示引用类型为 T 的元素。
3.Ptr: 这是迭代器的指针类型。与 Ref 类似,当你通过迭代器解引用(使用 -> 运算符)时,你希望得到的是元素的指针类型。因此,通常情况下 Ptr 被设定为 T*,表示指针类型为 T 的元素。

通过将这些参数设定为模板参数,ListIterator 类可以适用于不同类型的元素,同时也可以提供不同的引用和指针类型。这样做使得 ListIterator 类更加灵活,能够适用于不同的使用场景。

  • 封装的意义
    将迭代器的实现从 List 类中分离出来,有几个重要的意义和优势:
  1. 模块化设计:通过将迭代器封装为单独的类,可以实现更模块化的设计。这意味着 List 类的实现与迭代器的实现可以分开,每个类都专注于自己的职责。这样的设计使得代码更易于理解、维护和测试。
  2. 可重用性:通过将迭代器设计为独立的类,可以在不同的容器类中重复使用相同的迭代器实现。例如,如果你有另一个类似于 List 的容器类,也需要迭代器来遍历其中的元素,你可以直接重用相同的迭代器实现,而无需重新编写。
  3. 灵活性:将迭代器设计为独立的类使得它们的实现更加灵活。你可以在迭代器类中添加额外的功能或改变迭代器的行为,而不会影响到容器类的实现。这样的设计使得容器和迭代器的职责分离,每个类可以独立地演化和改进。
  4. 通用性:独立的迭代器类可以设计成通用的,适用于多种容器类型。这意味着你可以为不同的容器类实现相同的迭代器接口,使得用户在使用不同的容器时无需学习不同的迭代器接口,提高了代码的一致性和可用性。

总的来说,将迭代器封装为独立的类使得代码更加模块化、可重用、灵活和通用,提高了代码的可维护性、可扩展性和可读性。

🍃list类中迭代器的使用

public:
    typedef ListIterator<T, T&, T*> iterator;
    typedef ListIterator<T, const T&, const T&> const_iterator;
  • begin()和end()
// List Iterator
iterator begin()
{
    return _pHead->_pNext;
}

iterator end()
{
    return _pHead;
}

const_iterator begin() const
{
    return _pHead->_pNext;
}

const_iterator end() const
{
    return _pHead;
}
  • erase
    删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
    assert(pos._pNode != _pHead);
    Node* Prev = pos._pNode->_pPre;
    Node* Next = pos._pNode->_pNext;

    delete pos._pNode;
    Prev->_pNext = Next;
    Next->_pPre = Prev;

    return iterator(Next);
}

🍃List Modify

void push_back(const T& val) { insert(end(), val); }
void pop_back() { erase(--end()); }
void push_front(const T& val) 
{ 
    assert(!empty());
    insert(begin(), val); 
}
void pop_front() { erase(begin()); }

🍁全部代码

#pragma once
#include<assert.h>
#include<iostream>
using namespace std;

namespace My_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;
    };


    //List的迭代器类
    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;

        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}

        ListIterator(const Self& l)
        {
            _pNode = l._pNode;
        }

        T& operator*()
        {
            assert(_pNode != _pNode->_pNext);
            return _pNode->_val;
        }

        T* operator->()
        {
            return &(*this);
        }

        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }

        Self operator++(int)
        {
            PNode* tmp = _pNode;
            _pNode = _pNode->_pNext;
            return tmp;
        }

        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }

        Self& operator--(int)
        {
            PNode* tmp = _pNode;
            _pNode = _pNode->_pPre;
            return tmp;
        }

        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }

        bool operator==(const Self& l)
        {
            return !(*this != l);
        }
        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(const PNode& pHead = nullptr)
        {
            CreateHead();
            /*_pHead = new Node();
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;*/
        }

        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i = 0; i < n; ++i)
                push_back(value);

            /*int cnt = 0;
            while (cnt < n)
            {
                PNode _first = new Node(value);
                PNode tmp = _pHead->_pPre;
                tmp->_pNext = _first;
                _first->_pPre = tmp;
                _first->_pNext = _pHead;
                _pHead->_pPre = _first;
                ++cnt;
            }*/
        }

        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            CreateHead();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }

            /*while (first != last)
            {
                PNode _first = new Node(*first);
                PNode tmp = _pHead->_pPre;
                tmp->_pNext = _first;
                _first->_pPre = tmp;
                _first->_pNext = _pHead;
                _pHead->_pPre = _first;
                ++first;
            }*/
        }

        list(const list<T>& l)
        {
            CreateHead();
            list<T> temp(l.cbegin(), l.cend());
            swap(temp);

            /*iterator first = l._pHead->_pNext;
            iterator last = l._pHead;
            while (first != last)
            {
                PNode _first = new Node(*first);
                PNode tmp = _pHead->_pPre;
                tmp->_pNext = _first;
                _first->_pPre = tmp;
                _first->_pNext = _pHead;
                _pHead->_pPre = _first;
                ++first;
            }*/
        }

        list<T>& operator=(const list<T> l)
        {
            CreateHead();

            swap(l);
            return *this;
            /*iterator first = l._pHead->_pNext;
            iterator last = l._pHead;
            while (first != last)
            {
                PNode _first = new Node(*first);
                PNode tmp = _pHead->_pPre;
                tmp->_pNext = _first;
                _first->_pPre = tmp;
                _first->_pNext = _pHead;
                _pHead->_pPre = _first;
                ++first;
            }
            return *this;*/
        }

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

            /*Node* cur = _pHead->_pNext;
            Node* tmp = cur->_pNext;
            while (cur != _pHead)
            {
                delete cur;
                cur = tmp;
                tmp = tmp->_pNext;
            }
            tmp = cur = nullptr;
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;*/
        }


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

        iterator end()
        {
            return _pHead;
        }

        const_iterator begin() const
        {
            return _pHead->_pNext;
        }

        const_iterator end() const
        {
            return _pHead;
        }


        ///
        // List Capacity
        size_t size()const
        {
            Node* cur = _pHead->_pNext;
            size_t cnt = 0;
            while (cur != _pHead)
            {
                ++cnt;
                cur = cur->_pNext;
            }
            return cnt;
        }

        bool empty()const
        {
            return size() == 0;
        }


        
        // List Access
        T& front()
        {
            return _pHead->_pNext->_val;
        }

        const T& front()const
        {
            return _pHead->_pNext->_val;
        }

        T& back()
        {
            return _pHead->_pPre->_val;
        }

        const T& back()const
        {
            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) 
        { 
            assert(!empty());
            insert(begin(), val); 
        }
        void pop_front() { erase(begin()); }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            Node* cur = pos._pNode;
            Node* newnode = new Node(val);
            Node* prev = cur->_pPre;

            // prev  newnode  cur
            prev->_pNext = newnode;
            newnode->_pPre = prev;
            newnode->_pNext = cur;
            cur->_pPre = newnode;
            
            return iterator(newnode);
        }

        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            assert(pos._pNode != _pHead);
            Node* Prev = pos._pNode->_pPre;
            Node* Next = pos._pNode->_pNext;

            delete pos._pNode;
            Prev->_pNext = Next;
            Next->_pPre = Prev;

            return iterator(Next);
        }

        void clear()
        {
            iterator cur = begin();
            while (cur != end())
            {
                cur = erase(cur);
            }
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;
        }

        void swap(list<T>& l)
        {
            /*list<T> tmp = l;
            l = *this;
            *this = tmp;*/

            PNode tmp = _pHead;
            _pHead = l._pHead;
            l._pHead = tmp;
        }

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

        PNode _pHead;
    };
};

你学会了吗?
好啦,本章对于《C++:list模拟实现》的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!

如你喜欢,点点赞就是对我的支持,感谢感谢!!!

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1793970.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python接口自动化测试:Json 数据处理实战

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 上一篇说了关于json数据处理&#xff0c;是为了断言方便&#xff0c;这篇就带各位小伙伴实战一下…

vue3+elementPlus实现Radio单选切换显示不同内容

el-radio-group 组件方法&#xff1a; <template><el-radio-group v-model"radio"><el-radio :value"0">阶梯达标</el-radio><el-radio :value"1">限时达标</el-radio></el-radio-group> </templ…

ElementUI之el-tooltip显示多行内容

ElementUI之el-tooltip显示多行内容 文章目录 ElementUI之el-tooltip显示多行内容1. 多行文本实现2. 实现代码3. 展示效果 1. 多行文本实现 展示多行文本或者是设置文本内容的格式&#xff0c;使用具名 slot 分发content&#xff0c;替代tooltip中的content属性。 2. 实现代码 …

​谁用谁知道,教师实用工具分享​

老师们面临着日益增长的教学和管理任务。为了有效提升工作效率&#xff0c;一些实用的工具成为了老师们不可或缺的助手。给大家分享几款教师必备的工具&#xff0c;帮助教师们在教学和管理工作中更加得心应手。 1. 知乎&#xff1a;知识的海洋 知乎是一个中文问答社区&#xf…

在Jenkins 中使用 NVM 管理 Node.js 部署项目的自动化脚本

在Jenkins 中使用 NVM 管理 Node.js 部署项目的自动化脚本 人生旅途&#xff0c;总有人不断地走来&#xff0c;有人不断地离去。当新名字变成老名字&#xff0c;当老的名字渐渐模糊&#xff0c;又是一个故事的结束和另一个故事的开始。 在现代软件开发中&#xff0c;持续集成/持…

线上虚拟展厅的6大优势

随着科技的日新月异&#xff0c;线上VR虚拟展厅已崭露头角&#xff0c;成为企业创新的营销利器。它凭借沉浸式的交互体验、全球市场的无缝拓展以及显著降低的营销成本&#xff0c;为企业的营销活动带来了颠覆性的变革。那么&#xff0c;视创云展的线上VR虚拟展厅究竟具备哪些引…

NSSCTF-Web题目5

目录 [SWPUCTF 2021 新生赛]error 1、题目 2、知识点 3、思路 [LitCTF 2023]作业管理系统 1、题目 2、知识点 3、思路 [HUBUCTF 2022 新生赛]checkin 1、题目 2、知识点 3、思路 [SWPUCTF 2021 新生赛]error 1、题目 2、知识点 数据库注入、报错注入 3、思路 首先…

UE4 使用样条线做鱼儿封闭路径动画

描述&#xff1a;鱼儿的游动动画的特点 1.通常是始终保持Y (Pitch)轴角度不变 2.调头的时候改变的是Z轴角度 效果&#xff1a;调头的时候比较自然 蓝图&#xff1a; 为了让鱼儿有恒定的游动速度&#xff0c;增加以下蓝图节点&#xff0c;游动速度为50 最后&#xff0c;让鱼…

763. 划分字母区间(中等)

763. 划分字母区间 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;763. 划分字母区间 2.详细题解 划分字母片段&#xff0c;要求每个字母仅能出现在一个片段中&#xff0c;划分的片段数要最多。如果没有限制要最多的情况&#xff0c…

树-层序遍历序列构造二叉树

一、问题描述 二、实现思路 问题给出了层序遍历序列&#xff0c;我们使用队列来实现二叉树的构造过程&#xff1a; 这里注意&#xff1a;在写代码时&#xff0c;比较字符串数组内元素str和某个字符串是否相等时用str.equals("#")的操作&#xff0c;如果用 会引发比较…

派派派森03

1.JSON数据 Python数据和Json数据的相互转化 # 导入json模块 import json#准备符合json格式要求的python数据 data [{"name": "老王", "age": 16}, {"name": "张三", "age": 20}]# 通过json.dump(data)方法把pyt…

ES6-03-模版字符串、对象的简化写法

一、模版字符串 1-1、声明 反引号。 1-2、特性 1、字符串内容可以直接换行 得用号连接 2、变量拼接 现在&#xff1a; 二、对象的简化写法 ES6允许在大括号里面&#xff0c;直接写入变量和函数&#xff0c;作为对象的属性和方法。 let name milk;let chage function(){con…

文心一言 VS 讯飞星火 VS chatgpt (275)-- 算法导论20.3 2题

二、修改 vEB 树以支持带有卫星数据的关键字。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; vEB 树&#xff08;van Emde Boas 树&#xff09;是一种用于处理稀疏位集合的高效数据结构&#xff0c;它允许在常数时间内执行查找、插入、删除和前驱/后继操作。然…

SCT53600TVB具有反向电流保护的理想二极管控制器

4.7V至65V工作范围 – 65V反向额定电压 用于外部N沟道MOSFET的电荷泵 20mV正向压降调节 12V栅极驱动电压 带启用输入 驱动高侧外部N沟道MOSFET 1μA关断电流&#xff08;EN低&#xff09; 60μA工作静态电流&#xff08;EN高&#xff09; 2.3-A峰值…

zabbix“专家坐诊”第241期问答

问题一 Q&#xff1a;华为交换机的100GE 1/0/1口的光模块收光值监测不到&#xff0c;有没有人碰到过这个问题呢&#xff1f;其他的端口都能监测到收光值&#xff0c;但是100GE 1/0/1口监测不到收光值。底层能查到&#xff0c;zabbix 6.0监控不到&#xff0c;以下是端口的报错信…

EasyRecovery2024永久免费crack激活码注册码

在数字化时代&#xff0c;数据已经成为我们生活和工作中不可或缺的一部分。无论是个人用户还是企业用户&#xff0c;都面临着数据丢失的风险。一旦数据丢失&#xff0c;可能会给我们的工作带来极大的不便&#xff0c;甚至可能对企业造成重大损失。因此&#xff0c;数据安全和恢…

Unity VR 零基础开发之 Pico4 MR

一、新建Unity2021.3.37 3D工程 二、切换到Android安卓平台 1、点击Unity编辑器左上角的Flie后&#xff0c;选择Build Setting选项。 2、弹出弹窗后&#xff0c;点击Android选项&#xff0c;然后再点击Switch Platform按钮切换成安卓平台。 3、切换完成后Android选项后面会显示…

php7.3安装phalcon扩展

php7安装3.4版本的phalcon扩展 适用于Centos6.x和Centos7.x系统&#xff0c;php使用7.1版本&#xff0c;wlnmp一键包已支持该扩展 phalcon扩展包地址&#xff1a;https://github.com/phalcon/cphalcon &#xff08;git clone 有可能连接不上&#xff09; 1、安装所需依赖&a…

Hadoop3:MapReduce源码解读之Map阶段的TextInputFormat切片机制(3)

Job那块的断点代码截图省略&#xff0c;直接进入切片逻辑 参考&#xff1a;Hadoop3&#xff1a;MapReduce源码解读之Map阶段的Job任务提交流程&#xff08;1&#xff09; 5、TextInputFormat源码解析 类的继承关系 它的内容比较少 重写了两个父类的方法 这里关心一下泛型参数…

MySQL Shell 使用指南

前言&#xff1a; MySQL Shell 是官方提供的 MySQL 周边适配组件&#xff0c;是新一代的高级客户端&#xff0c;在 MySQL 8.0 及其以后的版本得以慢慢推广应用。之前笔者因为 MySQL 8.0 用得比较少&#xff0c;一直没有详细使用过这个工具&#xff0c;近期在捣鼓 MySQL 8.0&am…