当前位置: 首页 > news >正文

【C++】——list的介绍和模拟实现

P. S.:以下代码均在VS2019环境下测试,不代表所有编译器均可通过。
P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。

  

在这里插入图片描述

                                           博主主页:Yan. yan.
                                              C语言专栏
                                            数据结构专栏
                                         力扣牛客经典题目专栏
                                                     C++专栏

文章目录

  • 一、 list的介绍和使用
    • 1.1、list的介绍
    • 1.2、list的使用
      • 1.2.1、list的构造
      • 1.2.2、list iterator的使用
      • 1.2.3、list capacity(容量相关)
      • 1.2.4、list element access(元素访问)
      • 1.2.5、list modifiers(链表修改)
      • 1.2.6、list operation(对链表的一些操作)
  • 二、list的模拟实现
    • 2.1、list的节点
    • 2.2、list的成员变量
    • 2.3、list的迭代器
      • 2.3.1、普通迭代器
      • 2.3.2、const迭代器
    • 2.4、list的成员函数
      • 2.4.1、构造函数
      • 2.4.2、拷贝构造函数
      • 2.4.3、赋值运算符重载
      • 2.4.4、push_back
      • 2.4.5、迭代器相关
      • 2.4.6、 insert
      • 2.4.7、erase
      • 2.4.8、 push_front
      • 2.4.10、pop_front
      • 2.4.11、 size
      • 2.4.12、clear
      • 2.4.13、析构函数

一、 list的介绍和使用

1.1、list的介绍

  • list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  • list 的底层是双向链表结构,双向链表中的每个元素存储在互不相关的独立节点中,在节点中通过指针指向的前一个元素和后一个元素。

1.2、list的使用

  list的文本介绍
  list在实际中非常重要,在实际中我们熟悉常用的接口就可以,下面列出了需要我们重点掌握的接口。

1.2.1、list的构造

构造函数接口说明
list()list 的默认构造,构造空的 list
list(size_type n, const value_type& val = value_type())构造的 list 中包含 n 个值为 val 的元素
list(const list& x)拷贝构造函数
list(InputIterator first, InputIterator last)用[first,last)区间中的元素构造 list
void TestList1()
{list<int> l1;                         // 构造空的l1list<int> l2(4, 100);                 // l2中放4个值为100的元素list<int> l3(l2.begin(), l2.end());  // 用l2的[begin(), end())左闭右开的区间构造l3list<int> l4(l3);                    // 用l3拷贝构造l4// 以数组为迭代器区间构造l5int array[] = { 16,2,77,29 };list<int> l5(array, array + sizeof(array) / sizeof(int));// 列表格式初始化C++11list<int> l6{ 1,2,3,4,5 };// 用迭代器方式打印l5中的元素list<int>::iterator it = l5.begin();while (it != l5.end()){cout << *it << " ";++it;}cout << endl;// C++11范围for的方式遍历for (auto& e : l5)cout << e << " ";cout << endl;
}

1.2.2、list iterator的使用

函数声明接口说明
begin() + end()返回第一个元素的迭代器 + 返回最后一个元素下一个位置的迭代器
rebegin() + rend()返回第一个元素的 reverse_iterator,即 end 位置,返回最后一个一个元素下一个位置的 reverse_iterator,即 begin 位置

注意: begin 与 end 为正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动。rbegin 与 rend 为反向迭代器,对迭代器执行 ++ 操作,迭代器向前移动。由于 list 的底层物理空间并不连续,所以 list 的迭代器不再是原生指针,并且 list 的迭代器没有对 + 和 - 进行重载,只重载了 ++ 和 – ,因为空间不连续,重载 + 会比较复杂。即 l.begin() + 5 是不被允许的。

void PrintList(const list<int>& l)
{// 注意这里调用的是list的 begin() const,返回list的const_iterator对象for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it){cout << *it << " ";// *it = 10; 编译不通过}cout << endl;
}void TestList2()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));// 使用正向迭代器正向list中的元素// list<int>::iterator it = l.begin();   // C++98中语法auto it = l.begin();                     // C++11之后推荐写法while (it != l.end()){cout << *it << " ";++it;}cout << endl;// 使用反向迭代器逆向打印list中的元素// list<int>::reverse_iterator rit = l.rbegin();auto rit = l.rbegin();while (rit != l.rend()){cout << *rit << " ";++rit;}cout << endl;
}

1.2.3、list capacity(容量相关)

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

1.2.4、list element access(元素访问)

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

1.2.5、list modifiers(链表修改)

函数声明接口说明
push_front在 list 的第一个节点前插入值为 val 的节点
pop_front删除 list 中第一个节点
push_back在 list 尾部插入一个值为 val 的节点
pop_back删除 list 中最后一个节点
insert在 list 的 position 位置中插入一个值为 val 的节点
erase删除 list position 位置的节点
swap交换两个 list 的节点
clear清空 list 中的有效元素

1.2.6、list operation(对链表的一些操作)

函数声明接口说明
reverse对链表进行逆置
sort对链表中的元素进行排序(稳定排序)
merge对两个有序的链表进行归并,得到一个有序的链表
unique对链表中的元素去重
remove删除具有特定值的节点
splice将 A 链表中的节点转移到 B 链表

二、list的模拟实现

2.1、list的节点

template<class T>
struct ListNode
{ListNode<T>* _next;ListNode<T>* _prev;T _val;ListNode(const T& val = T()){_next = nullptr;_prev = nullptr;_val = val;}
};

2.2、list的成员变量

class list
{typedef ListNode<T> Node;
public://一些成员函数
private:Node* _head;
}

2.3、list的迭代器

  list 的迭代器不能再使用原生指针,如果 list 的迭代器使用原生指针的话,那对迭代器解引用得到的是一个节点,而我们希望对迭代器解引用可以得到节点里面存储的元素,并且 list 在底层的物理空间并不连续,如果使用原生指针作为 list 的迭代器,那对迭代器执行 ++ 操作,并不会让迭代器指向下一个节点。因此我们需要对 list 的迭代器进行封装,然后将一些运算符进行重载,以实现迭代器本该有的效果。

2.3.1、普通迭代器

template<class T>
struct _list_iterator
{typedef ListNode<T> Node;Node* _node;_list_iterator(Node* val){_node = val;}T& operator* (){return _node->_val;}T* operator-> ()//迭代器通过->应该指向节点中的元素,因此返回的是一个T类型的地址{return &(_node->_val);}bool operator!= (const _list_iterator<T>& right){return _node != right._node;}_list_iterator<T> operator++(){_node = _node->_next;return *this;}_list_iterator<T> operator++(int){_list_iterator<T> tmp(this->_node);_node = _node->_next;return tmp;}
};

  这里的类名不能直接叫 iterator,因为每种容器的迭代器底层实现可能都有所不同,即可能会为每一种容器都单独实现一个迭代器类,如果都直接使用 iterator,会导致命名冲突。其次,迭代器类不需要我们自己写析构函数、拷贝构造函数、赋值运算符重载函数,直接使用默认生成的就可以,言外之意就是这里使用浅拷贝即可,因为迭代器只是一种工具,它不需要对资源进行释放清理,资源释放清理工作是在容器类中实现的,浅拷贝的问题就出在会对同一块空间释放两次,而迭代器无需对空间进行释放,所以浅拷贝是满足我们需求的。

2.3.2、const迭代器

  上面我们实现了普通迭代器,那 const 迭代器该如何实现呢?直接在容器类里面写上一句 typedef const _list_iterator const_iterator 可以嘛?答案是不可以,const 迭代器本质是限制迭代器指向的内容不能修改,而 const 迭代器自身可以修改,它可以指向其他节点。前面这种写法,const 限制的就是迭代器本身,会让迭代器无法实现 ++ 等操作。那如何控制迭代指向的内容不能修改呢?可以通过控制 operator* 的返回值来实现。但是仅仅只有返回值类型不同,是无法构成函数重载的。那要怎样才能在一个类里面实现两个 operator* 让他俩一个返回普通的 T&,一个返回 const T& 呢?一般人可能想着那就再单独写一个 _list_const_iterator 的类,这样也行,就是会比较冗余,我们可以通过在普通迭代器的基础上,再传递一个模板参数,让编译器来帮们生成呀。除此之外, operator->也需要实现 const 版本,因此还需要第三个模板参数。

template<class T,class Ref, class Ptr>
struct _list_iterator
{typedef ListNode<T> Node;typedef _list_iterator<T, Ref, Ptr> self;Node* _node;_list_iterator(Node* val){_node = val;}Ref operator* (){return _node->_val;}Ptr operator-> (){return &(_node->_val);}bool operator!= (const self& right) const{return _node != right._node;}bool operator== (const self& right) const{return _node == right._node;}self operator++(){_node = _node->_next;return *this;}self operator++(int){self tmp(this->_node);_node = _node->_next;return tmp;}self operator--(){_node = _node->_prev;return *this;}self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}
};
//operator->的使用场景
struct A
{A(int a = 0, int b = 0){_a = a;_b = b;}int _a;int _b;
};void Textlist3()
{wcy::list<A> l;l.push_back(A(1, 2));l.push_back(A(3, 4));l.push_back(A(5, 6));l.push_back(A(7, 8));wcy::list<A>::iterator it = l.begin();while (it != l.end()){cout << it->_a << ',' << it->_b << " ";cout << endl;it++;}
}

2.4、list的成员函数

2.4.1、构造函数

list()
{_head = new Node;_head->_prev = _head;_head->_next = _next;
}

2.4.2、拷贝构造函数

list(const list& ll)
//list(const list<T>& ll)
{_head = new Node;_head->_prev = _head;_head->_next = _head;for (auto& e : ll){push_back(e);}
}

2.4.3、赋值运算符重载

void swap(list<T> l2)
{std::swap(_head, l2._head);
}list& operator=(const list ll)
//list<T>& operator=(const list<T> ll)
{//现代写法swap(ll);return *this;
}

2.4.4、push_back

void push_back(const T& val)
{//先找尾Node* tail = _head;while (tail->_next != _head){tail = tail->_next;}//插入元素Node* newnode = new Node(val);tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;
}

2.4.5、迭代器相关

iterator begin()
{return _head->_next;//单参数的构造函数支持隐式类型转换
}iterator end()
{return _head;
}const_iterator begin() const
{return _head->_next;//单参数的构造函数支持隐式类型转换
}const_iterator end() const
{return _head;
}

2.4.6、 insert

iterator insert(iterator pos, const T& val)
{//找到 pos 位置的前一个位置Node* cur = pos._node;Node* prev = cur->_prev;//插入元素Node* newnode = new Node(val);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return newnode;
}

2.4.7、erase

iterator erase(iterator pos)
{assert(pos != end());Node* cur = pos._node;//保存当前节点Node* prev = cur->_prev;//保存前一个节点Node* next = cur->_next;//保存后一个节点prev->_next = next;next->_prev = prev;delete cur;cur = nullptr;return next;
}

2.4.8、 push_front

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

2.4.10、pop_front

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

2.4.11、 size

size_t size()
{size_t sz = 0;iterator it = begin();while (it != end()){it++;sz++;}return sz;
}

2.4.12、clear

void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

2.4.13、析构函数

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

clear 和 析构函数的主要区别在于是否释放头节点。

相关文章:

【C++】——list的介绍和模拟实现

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;Yan. yan.                        …...

B树系列解析

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…...

docker 部署 WEB IDE

简介 问题描述&#xff1a;GitCode 的 Web IDE 不满足个人使用需求 如何解决&#xff1a;在本机或云服务器部署 Web IDE 如何解决 拉取容器镜像 docker pull coder/code-server 运行 docker run -d --name vscode -p 8080:8080 -p 8443:8443 -e PASSWORD"123456&quo…...

【Android】数据存储

本章介绍Android五种主要存储方式的用法&#xff0c;包括共享参数SharedPreferences、数据库SQLite、SD卡文件、App的全局内存&#xff0c;另外介绍重要组件之一的应用Application的基本概念与常见用法&#xff0c;以及四大组件之一的内容提供器ContentProvider的基本概念与常见…...

个人网络安全的几个重点与防御

1 浏览器 firefox 这是第一选择 如果你真的不明白可以找找各个浏览器漏洞 mail 的危险的 来自与代理和漏洞 浏览器溢出漏洞 实时注意更新就可以 2 防火墙 大家都用windows 只需在 gpedit.msc 设置 但有什么未知漏洞就不得而知了 因为美国的计划问题 网络端口溢出漏洞 但…...

python爬虫 - 初识爬虫

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、爬虫的关键概念 &#xff08;一&#xff09;HTTP请求与响应 &#xff0…...

tomcat版本升级导致的umask问题

文章目录 1、问题背景2、问题分析3、深入研究4、umask4.1、umask的工作原理4.2、umask的计算方式4.3、示例4.4、如何设置umask4.5、注意事项 1、问题背景 我们的java服务是打成war包放在tomcat容器里运行的&#xff0c;有一天我像往常一样去查看服务的日志文件&#xff0c;却提…...

Golang | Leetcode Golang题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; func findContentChildren(g []int, s []int) (ans int) {sort.Ints(g)sort.Ints(s)m, n : len(g), len(s)for i, j : 0, 0; i < m && j < n; i {for j < n && g[i] > s[j] {j}if j < n {ansj}}return }...

vscode+stfp插件,实现远程自动同步文件代码

概述 远程同步代码&#xff0c;将本地代码实时保存到同一局域网内的另一台电脑&#xff08;linux系统&#xff09;&#xff0c;这里的本地代码也可以是远程服务上的代码&#xff0c;即从一个远程ip同步到另一台远程ip服务器。 工具 vscode&#xff0c;SFTP插件 安装 vscod…...

python 实现djb2哈希算法

djb2哈希算法介绍 DJB2哈希算法是一种简单且快速的哈希算法&#xff0c;由Daniel J. Bernstein设计。这种算法的实现非常简单&#xff0c;适用于短键值的哈希表&#xff0c;也常被用于嵌入式设备和资源受限的系统。 基本原理 DJB2算法的原理是将输入的字符串视为一个字节数组…...

文件夹作为普通文件而非子模块管理

relaxed_ik_ros2 文件夹下存在 .gitmodules 文件和 .gitignore 文件。这说明该目录已经被 Git 识别为子模块。 要将这个文件夹作为普通文件而非子模块管理&#xff0c;你可以按以下步骤操作&#xff1a; 1. 删除子模块配置 首先删除 .gitmodules 文件中的子模块配置。你可以…...

7c结构体

文章目录 一、结构体的设计二、结构体变量的初始化2.1结构体在内存表示&#xff1b;**2.2**结构体类型声明和 结构体变量的定义和初始化只声明结构体类型声明类型的同时定义变量p1用已有结构体类型定义结构体变量p2*定义变量的同时赋初值。*匿名声明结构体类型 2.3 结构体嵌套及…...

浅聊前后端分离开发和前后端不分离开发模式

1.先聊聊Web开发的开发框架Spring MVC 首先要知道&#xff0c;Spring MVC是Web开发领域的一个知名框架&#xff0c;可以开发基于请求-响应模式的Web应用。而Web开发的本质是遵循HTTP&#xff08;Hyper Text Transfer Protocol: 超文本传输协议&#xff09;协议【发请求&#xf…...

RabbitMQ篇(死信交换机)

目录 一、简介 二、TTL过期时间 三、应用场景 一、简介 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff08;dead letter&#xff09; 消费者使用basic.reject或者basic.nack声明消费失败&#xff0c;并且消息的requeue参数设置为false消息是一个过…...

HBase 的 MemStore 详解

一、MemStore 概述 MemStore 是 HBase 的内存存储区域&#xff0c;它是一个负责缓存数据写入操作的组件。每当有写操作&#xff08;如 Put 或 Delete&#xff09;发生时&#xff0c;数据会首先被写入到 MemStore 中&#xff0c;而不是直接写入磁盘。MemStore 类似于数据库中的缓…...

【嵌入式软件-数据结构与算法】01-数据结构

摘录于老师的教学课程~~(*๓╰╯๓)~~内含链表、队列、栈、循环队列等详细介绍~~ 基础知识系列 有空再继续更~~~ 目录 【链表】 一、单链表 1、存储结构&#xff1a;带头结点的单链表 2、单链表结点类型的定义 3、创建单链表 1&#xff09;头插法 2&#xff09;尾插法 …...

Windows应用开发-解析AVI视频文件

本Windows应用解析AVI视频文件&#xff0c;以表格的方式显示AVI文件结构。并可以将结果保存到bmp图片。下面是&#xff0c;使用该应用解析一部AVI电影获得的图片。 应用开发信息 定义一个INFO结构&#xff0c;包含两个字符串对象&#xff0c;一个ULONGLONG变量&#xff0c;和…...

探索TCP协议的奥秘:Python中的网络通信

引言 在网络通信的世界里&#xff0c;TCP协议&#xff08;传输控制协议&#xff09;就如同一座桥梁&#xff0c;连接着数据的发送方和接收方。作为一名拥有20年实战经验的编码专家&#xff0c;我深知TCP协议在构建稳定、可靠的网络应用中的重要性。今天&#xff0c;我将带领大…...

每日学习一个数据结构-树

文章目录 树的相关概念一、树的定义二、树的基本术语三、树的分类四、特殊类型的树五、树的遍历六、树的应用场景 树的遍历一、前序遍历二、中序遍历三、后序遍历使用java代码实现遍历总结 树的相关概念 树是一种重要的非线性数据结构&#xff0c;在计算机科学中有着广泛的应用…...

简单PCL库读文件(linux vscode编译)

#include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/common/common.h> #include <iostream>int main(int argc, char** argv) {if (argc ! 2) {std::cerr << "请指定 PCD 文件路径" << std::endl;return -…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

【Veristand】Veristand环境安装教程-Linux RT / Windows

首先声明&#xff0c;此教程是针对Simulink编译模型并导入Veristand中编写的&#xff0c;同时需要注意的是老用户编译可能用的是Veristand Model Framework&#xff0c;那个是历史版本&#xff0c;且NI不会再维护&#xff0c;新版本编译支持为VeriStand Model Generation Suppo…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...