C++ STL之list的使用及模拟实现

news2024/10/7 12:27:48

文章目录

  • 1. 介绍
  • 2. list类的使用
    • 2.1 list类对象的构造函数
    • 2.2 list类对象的容量操作
    • 2.3 list类对象的修改操作
    • 2.4 list类对象的访问及遍历操作
  • 3. list类的模拟实现


1. 介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)。

图示:

在这里插入图片描述

想要具体了解其底层数据结构可以参照:线性表之双向链表

2. list类的使用

2.1 list类对象的构造函数

(constructor)构造函数代码功能说明
explicit list (const allocator_type& alloc = allocator_type());(默认构造函数)构造一个空的容器,没有任何元素。
explicit list (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());(填充构造函数)构造一个具有 n 个元素的容器。每个元素都是 val 的副本。
template <class InputIterator>
list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());
(范围构造函数)构造一个与范围[first, last)中的元素数量相同的容器,每个元素都是从该范围中的相应元素构造而来,顺序相同。
list (const list& x);(拷贝构造函数)构造一个与 x 中的每个元素副本相同顺序的容器。

实例:

// constructing lists
#include <iostream>
#include <list>

int main()
{
    // 按照上述描述的顺序使用的构造函数
    std::list<int> first;                                // empty list of ints
    std::list<int> second(4, 100);                       // four ints with value 100
    std::list<int> third(second.begin(), second.end());  // iterating through second
    std::list<int> fourth(third);                       // a copy of third

    // the iterator constructor can also be used to construct from arrays:
    int myints[] = { 16,2,77,29 };
    std::list<int> fifth(myints, myints + sizeof(myints) / sizeof(int));

    std::cout << "The contents of fifth are: ";
    for (std::list<int>::iterator it = fifth.begin(); it != fifth.end(); it++)
        std::cout << *it << ' ';

    std::cout << '\n';

    return 0;
}

输出结果:

在这里插入图片描述

2.2 list类对象的容量操作

函数名称代码功能说明
emptybool empty() const;返回 list 容器是否为空(即其大小是否为0)。
sizesize_type size() const;返回 list 容器中元素的个数。
max_sizesize_type max_size() const;返回 list 容器中可以容纳的最大元素的数量。

2.3 list类对象的修改操作

函数名称代码功能说明
assigntemplate <class InputIterator>
void assign (InputIterator first, InputIterator last);
void assign (size_type n, const value_type& val);
对 list 容器进行赋值,替换其当前内容,并相应地修改其大小。
push_frontvoid push_front (const value_type& val);在 list 容器开头插入一个新元素 val。
pop_frontvoid pop_front();删除 list 容器中第一个元素。
push_backvoid push_back (const value_type& val);在 list 容器最后插入一个新元素 val。
pop_backvoid pop_back();删除 list 容器中最后一个元素。
insertiterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
在指定位置 position 之前插入新元素 val、n 个 val或者迭代器区间[first, last)范围的元素。
eraseiterator erase (iterator position);
iterator erase (iterator first, iterator last);
删除 position 位置的元素或者迭代器区间[first, last)范围的元素。
swapvoid swap (list& x);与另一个相同类型的 list 容器 x 交换内容。存在一个同名的非成员函数 swap,重载该算法的意义是优化交换时间。
resizevoid resize (size_type n, value_type val = value_type());改变容器的大小,使其包含 n 个元素。如果 n 小于当前容器的大小,则内容将被缩减为其前 n 个元素,并移除超出范围的元素。如果 n 大于当前容器的大小,则通过在末尾插入所需数量的元素来扩展内容,使其大小达到 n。如果指定了 val,则新元素将被初始化为 val 的副本;否则,它们将进行值初始化。
clearvoid clear();从 list 容器中移除所有元素,使容器的大小变为0。

2.4 list类对象的访问及遍历操作

函数名称代码功能说明
frontreference front();
const_reference front() const;
返回 list 容器中第一个元素的引用。
backreference back();
const_reference back() const;
返回 list 容器中最后一个元素的引用。

遍历操作

#include <iostream>
#include <list>

int main()
{
	std::list<int> lt(10, 100);

	// 1. 迭代器遍历
	for (std::list<int>::iterator it = lt.begin(); it != lt.end(); ++it)
		std::cout << *it << " ";
	std::cout << '\n';

	// 2. 范围for遍历
	for (auto e : lt)
		std::cout << e << " ";
	std::cout << '\n';

	return 0;
}

运行结果:

在这里插入图片描述

解释

  1. 迭代器遍历:
  • 首先,通过lt.begin()获取列表的起始迭代器,lt.end()获取列表的结束迭代器。
  • 使用std::list<int>::iterator it定义一个迭代器变量,并将其初始化为列表的起始迭代器。
  • 使用迭代器变量it进行循环迭代,从列表的起始位置迭代到结束位置(不包括结束位置)。
  • 在循环中,通过*it访问当前迭代器指向的元素,并输出到标准输出流std::cout中。
  1. 范围for循环遍历:
  • 使用auto关键字和范围for循环语法,遍历列表lt中的每个元素。
  • 在每次迭代中,将当前元素赋值给变量e
  • 在循环体内,输出变量e的值到标准输出流std::cout中。

3. list类的模拟实现

#pragma once
#include<iostream>
using namespace std;

namespace my_list
{   
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())
            :_pPre(nullptr)
            ,_pNext(nullptr)
            ,_val(val)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

    //List的迭代器类
    template<class T, class Ref, class Ptr>
    class List_iterator
    {
        typedef ListNode<T>* PNode;
        typedef List_iterator<T, Ref, Ptr> Self;
    public:
        List_iterator(PNode pNode = nullptr)
        {
            _pNode = pNode;
        }

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

        Ref operator*()
        {
            return _pNode->_val;
        }

        Ptr operator->()
        {
            return &_pNode->_val;
        }

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

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

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

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

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

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

    private:
        PNode _pNode;
    };

    // 反向迭代器——对正向迭代器的接口进行包装
    template<class Iterator, class Ref, class Ptr>
    struct Reverse_iterator
    {
        Iterator _it;
        typedef Reverse_iterator<Iterator, Ref, Ptr> Self;

        Reverse_iterator() {}

        Reverse_iterator(Iterator it)
            : _it(it)
        {}

        Ref operator*()
        {
            Iterator tmp(_it);
            --tmp;
            return *tmp;
        }

        Ptr operator->()
        {
            return &(operator*());
        }

        Self& operator++()
        {
            --_it;
            return *this;
        }

        Self operator++(int)
        {
            Self tmp(*this);
            --_it;
            return tmp;
        }

        Self& operator--() {
            ++_it;
            return *this;
        }

        Self operator--(int)
        {
            Self tmp(*this);
            ++_it;
            return tmp;
        }

        bool operator!=(const Self& s)
        {
            return _it != s._it;
        }

        bool operator==(const Self& s)
        {
            return _it == s._it;
        }
    };

    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef List_iterator<T, T&, T*> iterator;
        typedef List_iterator<T, const T&, const T&> const_iterator;
        typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
        typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

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

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

        const_iterator end() const
        {
            return const_iterator(_pHead);
        }

        reverse_iterator rbegin()
        {
            return reverse_iterator(end());
        }

        reverse_iterator rend()
        {
            return reverse_iterator(begin());
        }

        const_reverse_iterator rbegin() const
        {
            return const_reverse_iterator(end());
        }

        const_reverse_iterator rend() const
        {
            return const_reverse_iterator(begin());
        }

        ///
        // List 构造/赋值
        list()
        {
            CreateHead();
        }

        list(int n, const T& value = T())
        {
            CreateHead();
            while (n--)
            {
                push_back(value);
            }
            _size = n;
        }

        list(int n, T& value = T())
        {
            CreateHead();
            while (n--)
            {
                push_back(value);
            }
            _size = n;
        }

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

        list(const list<T>& l)
        {
            CreateHead();
            for (auto it : l)
            {
                push_back(it);
                _size++;
            }
        }

        void swap(list<T>& l)
        {
            std::swap(this->_pHead, l._pHead);
            std::swap(this->_size, l._size);
        }
        
        list<T>& operator=(list<T> l)
        {
            swap(l);
            return *this;
        }

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

        ///
        // List Capacity
        size_t size()const
        {
            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 tmp = new Node(val);
            PNode cur = pos.GetNode();
            PNode pre = cur->_pPre;
            tmp->_pNext = cur;
            tmp->_pPre = pre;
            pre->_pNext = tmp;
            cur->_pPre = tmp;
            _size++;
            return tmp;
        }

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

        void clear()
        {
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }
        
    private:
        // 创建头结点
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pNext = _pHead->_pPre = _pHead;
        }

        PNode _pHead;
        size_t _size = 0;
    };
};

注意

这里实现的反向迭代器类模板同样也可以适配到vector类和string类中。

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

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

相关文章

智慧工厂视频监控平台EasyCVR公网收流后内网设备无法播放是什么原因?

安防视频监控平台EasyCVR采用了开放式的网络结构&#xff0c;支持高清视频的接入和传输、分发&#xff0c;平台提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;此外&am…

OSPF基础华为ICT网络赛道

6.1.OSPF协议概述 由协议之中OSPF(Open Shortest Path First,开放式最短路径优先)协议是使用场 景非常广泛的动态路由协议之一。 OSPF在RFC2328中定义&#xff0c;是一种基于链路状态算法的路由协议。 静态路由是由工程师手动配置和维护的路由条目&#xff0c;命令行简单明确…

XTuner InternLM-Chat 个人小助手认知微调实践

1.概述 目标&#xff1a;通过微调&#xff0c;帮助模型了解对自己身份 方式&#xff1a;使用XTuner进行微调 2.实操 2.1微调环境准备 参考&#xff1a; XTuner复现-CSDN博客 # InternStudio 平台中&#xff0c;从本地 clone 一个已有 pytorch 2.0.1 的环境&#xff08;后…

Linux 部署mongodb

Linux 部署mongodb 一、mongodb安装包下载二、mongodb安装三、连接测试 linux tar包方式部署mongodb 一、mongodb安装包下载 查看系统版本与架构 cat /etc/redhat-release uname -mhttps://www.mongodb.com/download-center/community?jmpdocs下载对应操作系统版本 选择保存…

题记(26)--Sharing(链表公共后缀)

目录 一、题目内容 二、输入描述 三、输出描述 四、输入输出示例 五、完整C语言代码 一、题目内容 To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if…

RuoYi-Cloud本地部署--详细教程

文章目录 1、gitee项目地址2、RuoYi-Cloud架构3、本地部署3.1 下载项目3.2 idea打开项目3.3 启动nacos3.4 若依数据库准备3.5 启动redis3.6 修改nacos中的各个模块的配置文件3.7 启动ruoyi前端项目3.8 启动各个微服务模块 4、启动成功 1、gitee项目地址 https://gitee.com/y_p…

JSP的学习

1.JSP概念: Java服务端页面;一种动态的网页技术,既可以定义HTML,JS,CSS等静态内容,也可以定义Java代码的动态内容;JSPHTMLJava;JSP的作用:简化开发,避免了在Servlet中直接输出HTML标签; 2.JSP快速入门 3.JSP原理 概念&#xff1a;Java Server Pages&#xff0c;Java服务端页…

AI分割一切模型SAM(Segment Anything Model)的C++部署

2023年最火爆的分割模型莫过于SAM&#xff0c;截止今天2024年1月19日&#xff0c;github上的star已经达到了41.7k的惊人数量。下面我们来体会一下如何运行这个模型&#xff0c;以及如何用C部署这个模型。 检查cuda环境 我的Cuda版本是12.0.1&#xff0c;如下&#xff0c; Cudn…

[Tomcat] [最全] 目录和文件详解

打开tomcat的解压之后的目录可以看到如下的目录结构&#xff1a; Bin bin目录主要是用来存放tomcat的命令&#xff0c;主要有两大类&#xff0c;一类是以.sh结尾的&#xff08;linux命令&#xff09;&#xff0c;另一类是以.bat结尾的&#xff08;windows命令&#xff09;。 …

策略模式【结合Spring框架实践】

Hello!~大家好啊,很高兴我们又见面了,今天我们一起学习设计模式–【策略模式】 初次对此模式不懂的,或者想偷懒的,我强烈建议大家跟着我的一起把概念和代码一起敲一遍!~为啥子??因为我就是这样学会的,哈哈哈! 1.首先我们看下此模式的整体UML图 selector:选择器又叫做上下文co…

第五课:MindSpore自动并行

文章目录 第五课&#xff1a;MindSpore自动并行1、学习总结&#xff1a;数据并行模型并行MindSpore算子级并行算子级并行示例 流水线并行GPipe和Micro batch1F1B流水线并行示例 内存优化重计算优化器并行 MindSpore分布式并行模式课程ppt及代码地址 2、学习心得&#xff1a;3、…

Android 内存优化 内存泄漏

内存抖动 内存抖动是由于短时间内有大量对象进出新生区导致的&#xff0c;内存忽高忽低&#xff0c;有短时间内快速上升和下落的趋势&#xff0c;分析图呈锯齿状。 它伴随着频繁的GC&#xff0c;GC 会大量占用 UI 线程和CPU 资源&#xff0c;会导致APP 整体卡顿&#xff0c;甚…

Jupyter Notebook安装使用教程

Jupyter Notebook 是一个基于网页的交互式计算环境&#xff0c;允许你创建和共享包含代码、文本说明、图表和可视化结果的文档。它支持多种编程语言&#xff0c;包括 Python、R、Julia 等。其应用场景非常广泛&#xff0c;特别适用于数据科学、机器学习和教育领域。它可以用于数…

安装ROS2-ubuntu

相较于ROS1&#xff0c;ROS2在设计之初就考虑了在产品环境下⾯临的⼀些挑战&#xff0c;具体来说&#xff0c;ROS2采⽤&#xff08;或者计划采⽤&#xff09;以下策略以提升其在产品环境的适⽤度&#xff1a; ⽀持多机器⼈ 对⼩型嵌⼊式设备和微控制器的⽀持 实时系统&am…

《WebKit 技术内幕》学习之四(1): 资源加载和网络栈

第四章 资源加载和网络栈 使用网络栈来下载网页和网页资源是渲染引擎工作的第一步 1.WebKit 资源加载机制 1.1 资源 网页本身就是一种资源、网页还需要依赖很多其他的资源(图片、视频) &#xff08;1&#xff09;HTML 支持的资源主要包括以下几种类型&#xff1a; HTML 页…

Linux Shell alias的简单用法:给shell起别名

alias&#xff1a;显示该用户所有起过别名的命令 alias lla‘ls -al’&#xff1a;给ls -al起别名为lla unalias lla&#xff1a;取消lla的别名 1、该命令所有的操作只对个人用户生效&#xff0c;给普通用户起的别名在root用户下不生效&#xff0c;只有回到普通用户才生效。 2…

JOSEF约瑟 中间继电器JZ14-44Z/4 不带外罩和接线座

系列型号 JZ14-014Z/0中间继电器;JZ14-014Z/1中间继电器; JZ14-014Z/2中间继电器;JZ14-014Z/4中间继电器; JZ14-014J/0中间继电器;JZ14-014J/1中间继电器; JZ14-014J/2中间继电器;JZ14-014J/3中间继电器; JZ14-014J/4中间继电器;JZ14-140Z/0中间继电器; JZ14-140Z/1中间继…

[UI5 常用控件] 01.Text

文章目录 前言1. 普通文本2. 长文本&#xff1a;3. 设置最大显示行数 ( maxLines3 )4. 单行显示 ( wrappingfalse )5. 显示空白符 ( renderWhitespacetrue )6. 使用 - 连接单词:只适用于英文 ( wrappingTypeHyphenated )7. 空白时使用 - 代替 ( emptyIndicatorModeOn )8. JSON数…

Linux之权限(内容详细,细节满满)

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 Linux 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力 目录 一.前言 二.权限修改的两种方法 …

爬虫入门到精通_基础篇1(爬虫基本原理讲解, Urllib库基本使用)

01 爬虫基本原理讲解 1.什么是爬虫:请求网站并提取数据的自动化程序 2.爬虫基本流程&#xff1a; 发起请求&#xff1a;通过HTTP库向目标站点发起请求&#xff0c;即发送一个Request,请求可以包含额外的headers等信息&#xff0c;等待服务器响应。获取响应内容&#xff1a;如…