初识C++|list类的使用及模拟实现

news2024/11/15 20:07:49

🍬 mooridy-CSDN博客

🧁C++专栏(更新中!)

目录

list的介绍

list的使用

list的构造

list 容量

list 访问

list 增删查改

迭代器

迭代器失效问题

list模拟实现

list与vector的对比

emplace_back和push_back的区别


list的介绍

list底层——带头双向循环链表

list的使用

list的构造

构造函数( (constructor)接口说明
list (size_type n, const value_type& val =
value_type())
构造的 list 中包含 n 个值为 val
元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)
[first, last) 区间中的元素构造
list

list<int> lt1;                                // 构造空列表
list<int> lt2 (4,100);                       // 构造有4个100的列表
list<int> lt3 (lt2.begin(),lt2.end());  // 用迭代器构造
list<int> lt4 (third);                       // 拷贝构造

list 容量

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

list 访问

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

list 增删查改

函数声明接口说明
push_frontlist首元素前插入值为val的元素
pop_front删除list中第一个元素
push_backlist尾部插入值为val的元素
pop_back删除list中最后一个元素
insertlist position 位置前插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素
list<int> first (3,100);   
list<int> second (5,200);  
 it = first.begin();
first.insert(it,5);//在it前加1个5
first.insert(it,2,6);//在it前加2个6
  first.swap(second);

迭代器

1.

之前我们学习vector容器时可以简单的将迭代器理解为指针。然而在list中,这有的理解显然是有失偏颇的。如list的介绍中所提到的,list的底层实现是带头双向循环链表,而我们知道链表的空间是不连续的,如果还以为迭代器是指针,那么++的位置只能是结点的下一个位置,但这个理解显然不对。

那又为什么我们却能像vector一样的去使用list的迭代器呢?
而且*就是访问的数据,++和- -就能找到结点的后一个结点和前一个结点。

我们能用类似vector迭代器的方式操作list迭代器,是因为C++的类封装和操作符重载。迭代器封装了复杂的容器遍历逻辑,通过重载++--*等操作符,让我们能以直观方式访问元素,而无需关心其底层实现细节。

具体是如何实现的,可以看看下面的list模拟实现部分。

2.

在使用过程中,需注意list的迭代器只有++,--的功能,一步步的移动迭代器,是无法像vector的迭代器一样实现+或-功能进行跨越的加减。

迭代器按性质划分:

性质代表容器功能
单向ForwardIteratorforward_lisr/unordered_map...++
双向BidirectionalIteratorlist/map/set...++/--
随机RandomAcessIteratorvector/string/deque++/--/+/-

底层结构可以决定使用哪些算法。

迭代器失效问题

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

insert不会失效——只是修改了指针指向关系,而没有修改迭代器本身

erase——指向删除元素的迭代器失效

list模拟实现

//list.h
#include<iostream>
using namespace std;


namespace AA
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T()){
            _val = val;
            _prev = nullptr;
            _next=nullptr;
        }
        ListNode<T>* _prev;
        ListNode<T>* _next;
        T _val;
    };


    //List的迭代器类
    template<class T,class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T> Node;
        typedef ListIterator<T,Ref,Ptr> Self;
    public:
        ListIterator(Node* pNode = nullptr) {
            _pNode = pNode;
        }
        ListIterator(const Self& l) {
            *this = l;
        }
        Ref operator*() {
            return _pNode->_val;
        }
        Ptr operator->() {
            return &_pNode->_val;
        }
        Self& operator++() {
            _pNode = _pNode->_next;
            return *this;
        }
        Self operator++(int) {
            Self* tmp(*this);
            _pNode = _pNode->_next;
            return tmp;
        }
        Self& operator--() {
            _pNode = _pNode->_prev;
            return *this;
        }
        Self& operator--(int) {
            Self* tmp(*this);
            _pNode = _pNode->_prev;
            return tmp;
        }
        bool operator!=(const Self& l) {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l) {
            return _pNode == l._pNode;
        }
    private:
        Node* _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() {
            _pHead = new Node;
            _pHead->next = _pHead;
            _pHead->prev = _pHead;
            _size = 0;
        }
        list(int n, const T& value = T()) {
            _pHead = new Node;
            Node* pcur = _pHead;
            for (int i = 0; i < n; i++) {
                Node* newnode = new Node;
                newnode->val = value;
                newnode->next = _pHead;
                newnode->prev = pcur;
                pcur->next = newnode;
                pcur = newnode;
                _pHead->prev = pcur;
          
            }
            _size = n;
        }
        template <class Iterator>
        list(Iterator first, Iterator last);
        list(const list<T>& l);
        list<T>& operator=(const list<T> l);
        ~list() {
            clear();
            delete _pHead;
            _pHead = nullptr;
            _size = 0;
        }


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


        ///
        // List Capacity
        size_t size()const {
            return _size;
        }
        bool empty()const {
            return size == 0;
        }


        
        // List Access
        T& front() {
            return _pHead->next->_val;
        }
        const T& front()const {
            return _pHead->next->_val;
        }
        T& back() {
            return _pHead->prev->_val;
        }
        const T& back()const {
            return _pHead->prev->_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) {
            Node* newnode = new Node;
            newnode->_val = val;
            Node* pcur = pos._pNode;
            Node* prev = pcur->_prev;
            newnode->_next = pcur;
            newnode->_prev = prev;
            prev->_next = newnode;
            pcur->_prev = newnode;
            ++_size;
            return newnode;
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos) {
            Node* pcur = pos._pNode;
            Node* prev = pcur->_prev;
            Node* next = pcur->_next;
            delete pcur;
            pcur = nullptr;
            prev->_next = next;
            next->_prev = prev;

            --_size;
            return next;
        }
        void clear() {
            Node* pcur = _pHead->next;
            while (pcur!=_pHead) {
                pcur = erase(pcur);
            }
        }
        void swap(list<T>& l) {
            std::swap(_pHead, l._pHead);
            std::swap(_size, l._size);
        }
    private:
        size_t _size;
        Node* _pHead;
    };
};

list与vector的对比

vectorlist
底层结构动态顺序表,一段连续空间
带头结点的双向循环链表
随机访问
支持随机访问,访问某个元素效率 O(1)
不支持随机访问,访问某个元 素效率 O(N)
插入和删除
任意位置插入和删除效率低,需要搬移元素,时间 复杂度为 O(N) ,插入时有可能需要增容,增容需要 开辟新空间,拷贝元素,释放旧空间,导致效率更
任意位置插入和删除效率高,
不需要搬移元素,时间复杂度
O(1)
空间利用率
底层为连续空间,不容易造成内存碎片,空间利用 率高,缓存利用率高
底层节点动态开辟,小节点容 易造成内存碎片,空间利用率 低,缓存利用率低
迭代器
原生态指针
对原生态指针 ( 节点指针 ) 进行 封装
迭代器失效
在插入元素时,要给所有的迭代器重新赋值,因为 插入元素有可能会导致重新扩容,致使原来迭代器 失效,删除时,当前迭代器需要重新赋值否则会失
插入元素不会导致迭代器失 效,删除元素时,只会导致当 前迭代器失效,其他迭代器不 受影响
应用场景
需要高效存储,支持随机访问,不关心插入删除效
大量插入和删除操作,不关心 随机访问

emplace_back和push_back的区别

push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);

而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。这样效率更高。

同时在使用上,有下图代码所展示的小区别:

请注意:emplace_back() 是 C++ 11 标准新增加的,如果程序需要兼顾之前的版本,还是应该使用 push_back()。

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

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

相关文章

回溯法-n皇后

N皇后问题 问题定义 棋盘: 一个nn的网格。皇后: 一种特殊棋子&#xff0c;可以攻击同一行、同一列或两条对角线上的任何棋子。目标: 在棋盘上放置n个皇后&#xff0c;使得它们之间没有任何一个能够攻击到对方。 问题难点 确保皇后之间不在同一行或列。避免皇后在对角线上相…

英伟达:相同的剧本

财报超预期&#xff0c;盘后却大跌8%&#xff0c;最近好公司好像都犯了这种病。 英伟达在美股财报季压轴登场&#xff0c; 营收净利那可都是三位数的增长&#xff0c;再创新高。 都说炒股看的的是未来&#xff0c;在英伟达这贯彻地很彻底&#xff0c;业绩爆表只能算及格&#…

【操作系统】有A、B和C三个作业同时到达,执行时间分别为4,3,6,且在系统中以单道方式运行,则可以获得最短的平均周转时间的执行顺序为()。

目录 题目分析答案类似题 题目 有A、B和C三个作业同时到达&#xff0c;执行时间分别为4,3,6&#xff0c;且在系统中以单道方式运行&#xff0c;则可以获得最短的平均周转时间的执行顺序为&#xff08;&#xff09;。 分析 周转时间&#xff1a;程序从进入系统到完成的时间总…

nodejs基于微信小程序的书籍销售系统论文源码调试讲解

2 开发环境与相关技术 2.1 NODEJS技术 Nodejs语言是目前使用率最高的一个语言类程序&#xff0c;并且他的代码还是开源的&#xff0c;任何的软件开发者都可以进行使用&#xff0c;目前已经在人类计算机编程语言发展史上产生了深远影响。所以Nodejs语言是很成熟的&#xff0c;将…

平价运动耳机品牌推荐有哪些?五大爆款推荐,小白购前必看

对于很多人来说&#xff0c;运动可能是为了减肥&#xff0c;但是对我而言&#xff0c;运动从来不是为了身材焦虑&#xff0c;而是为了享受挥洒汗水后的畅快淋漓&#xff0c;尤其在天气渐暖的时节&#xff0c;约上三五好友&#xff0c;一起在夕阳下奔跑&#xff0c;在微风中骑行…

在线流程图制作指南:轻松绘制高质量流程图的方法!

流程图作为一种过程诊断工具&#xff0c;广泛应用于工作和生活中。无论是软件程序的算法流程图、请假审批流程图、产品工艺流程图&#xff0c;还是医院就诊流程等&#xff0c;流程图都能直观地描述具体的工作步骤&#xff0c;帮助决策者识别问题并制定解决方案。本文将通过即时…

800道软件测试面试题与答案+pdf+在线版

2024年软件测试行情不行&#xff0c;今年很多人想着金九银十换一个好工作&#xff0c;几次面试总感觉很多东西明明记住了&#xff0c;突然又忘了。 在整理资料的时候&#xff0c;被我发现一个宝藏内容&#xff01;&#xff01;⚠ 如何准备好面试&#xff0c;大家都头疼我总结…

C++语法基础(二)

C复合类型 结构体 1. C的结构&#xff0c;定义结构体类型的变量时&#xff0c;可以省略struct关键字 2. 可以定义成员函数&#xff0c;在结构体中的成员函数内部可以直接访问本结构体的成员&#xff0c;无需通过“.”或“->” 联合 1. C的联合,定义联合体类型的变…

聊聊Promise,catch和then的关系,rejected状态如何在then链中”透传“直到被处理

Promise在前端开发中用的很多了&#xff0c;感觉好像很熟了&#xff0c;但真的有些细节追究起来好像又有点是是而非。 今天聊聊Promise中的then和catch&#xff0c;以下面这个代码片段为例&#xff0c;如果能正确说出打印的内容&#xff0c;说明已经掌握了&#xff0c;后面的内…

Linux教程六:linux系统目录介绍

一、Linux系统目录介绍 1、关于目录的命令行讲解 需要确保自己使用了root账号登陆 cd / # 进入根目录 cd 目录路径 #进入到指定目录中去 #路径有绝对路径和相对路径&#xff0c;在Linux中&#xff0c;绝对路径以/开头ll #列举当前目录下所有文件和文件夹 &#xff08;ls -l…

活动报道 | 盘古信息亮相东莞中小数转供需对接会(滨海片区),深度剖析典型案例

为积极响应国家关于加快中小企业数字化转型的号召&#xff0c;推动东莞市中小企业数字化进程&#xff0c;8月29日&#xff0c;由东莞市工业和信息化局主办&#xff0c;长安镇经济发展局承办&#xff0c;东莞市软件行业协会协办的东莞市中小企业数字化转型城市试点供需对接会&am…

Vue3中的defineExpose的认识

文章目录 defineExpose子组件父组件&#xff1a;总结&#xff1a; defineExpose 使用 <script setup> 的组件是默认关闭的——即通过模板引用或者 $parent 链获取到的组件的公开实例&#xff0c;** 不会 **暴露任何在 <script setup> 中声明的绑定。 可以通过 def…

BH1750光照传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.工作原理&#xff1a;结构框图 三、程序设计 main.c文件 bh1750.h文件 bh1750.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 BH1750是一款数字型光照强度传感器&#xff0c;能够获取周围环境的光照强度。内置16bitAD转…

通帆科技“液氢微型发电站”:点亮氢能产业新征程

新质生产力的蓬勃发展正以磅礴之力推动产业的转型升级&#xff0c;氢能产业作为新质生产力的璀璨之星&#xff0c;成为新能源领域的关键增长极。 8 月 28 日&#xff0c;通帆新能源科技&#xff08;山东&#xff09;有限公司精心研发的 500kw “液氢微型发电站”产品成功下线交…

【C/C++】C语言中的内存分布

在C语言中&#xff0c;内存分布主要可以分为以下几个区域&#xff1a; 栈&#xff08;Stack&#xff09;&#xff1a;由编译器自动分配和释放&#xff0c;存放函数的参数值、局部变量的值等。 堆&#xff08;Heap&#xff09;&#xff1a;一般由程序员分配和释放&#xff0c;若…

电容应用原理

电容器是电子电路中不可或缺的元件&#xff0c;其在电路中承担的任务繁多&#xff0c;既可以用作储能元件&#xff0c;也能用于滤波、旁路和去耦。 电容的基本原理 电容的基本工作原理可以理解为电荷的存储和释放。电容器由两块金属板和夹在中间的绝缘介质构成&#xff0c;当…

Unity 中使用SQLite数据库

文章目录 0.参考文章1.Presentation —— 介绍2.&#xff08;SQLite4Unity3d&#xff09;Unity中直接使用SQLite的插件3.创建数据库4.创建表5.Navicat Premium&#xff08;数据库可视化&#xff09;6.增删改查6.1 增6.2 删6.3 改6.4 查 0.参考文章 https://blog.csdn.net/Chin…

结合系统架构设计的非功能性需求开发一套文化用品商城系统

案例 阅读以下关于软件系统设计的叙述&#xff0c;在答题纸上回答问题 1 至问题 3。 【题目】 某文化产业集团委托软件公司开发一套文化用品商城系统&#xff0c;业务涉及文化用品销售、定制、竞拍和点评等板块&#xff0c;以提升商城的信息化建设水平。该软件公司组织项目组完…

2024最新盘点:这12款plm项目管理系统值得推荐!

本文将盘点主流的plm项目管理系统&#xff0c;为企业选型提供参考 。 高效的plm项目管理系统是确保工程顺利进行、按时交付以及控制成本的关键&#xff0c;据美国建筑行业研究院的研究数据表明&#xff0c;实施高效项目管理的建筑企业&#xff0c;能够将项目成本降低 5%-10%。我…

1.7 离散频率

1.7 离散频率 离散时间和采样率 模拟到数字转换器 (ADC) 对连续时间信号进行采样以生成离散时间样本。对于数字信号处理器来说&#xff0c;该信号仅存储在内存中作为一系列数字。因此&#xff0c;采样率 F S F_S FS​ 的知识是数字域中信号处理的关键。 对于时间而言&#…