C++ | list

 前言

本篇博客讲解c++STL中的list

💓 个人主页:普通young man-CSDN博客

⏩ 文章专栏:C++_普通young man的博客-CSDN博客

⏩ 本人giee:   普通小青年 (pu-tong-young-man) - Gitee.com

      若有问题 评论区见📝

🎉欢迎大家点赞👍收藏⭐文章

本篇文章主要讲解list的用法和list的代码实现,这个list的用法和vector string的接口用法都差不多,所以我不会讲解太多,如果大家有疑问就去看我以前的博客

目录

list的介绍及使用

 1,介绍

2,使用

list的构造

list iterator的使用

list capacity

list element access

Modifiers:

list模拟实现

解析

1. 基础结构定义

list_node 结构体

2. 迭代器定义

list_iterator 结构体

3. 链表类定义

list 类

4. list 类的成员函数解析

构造与析构

插入与删除

迭代器操作

其他操作

5. 辅助函数

总结

注意事项


list的介绍及使用

 1,介绍

list - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/list/list/?kw=list

  

这里可以看出list是一个双向带环的链表

2,使用

list的接口和我们之前的vector和string的用法差不多,以下我展示一些常见的接口

list的构造

构造函数(list::list - C++ Reference (cplusplus.com))描述
list()构造一个空的 std::list
list(size_type n, const value_type& val = value_type())构造一个包含 n 个值为 val 的元素的 std::list。如果 val 没有显式给出,则使用默认构造的值。
list(const list& x)拷贝构造函数,复制另一个 std::list 实例的内容。
list(InputIterator first, InputIterator last)从输入迭代器范围 [first, last) 构造一个新的 std::list,其中 first 和 last 分别是输入序列的开始和结束迭代器。

演示代码

template <class T>
void Print(const list<T>& tmp) {for (auto it : tmp){cout << it << " ";}cout << endl;
}
void test1() {//list<int> a1{ 1,2,3,4,5,6 };list<int> a1(10, 1);list<int> a2(a1.begin(),a1.end());list<int> a3(10, 2);a1 = a2;Print(a1);Print(a2);Print(a3);}

list iterator的使用

函数声明描述
begin()返回指向列表中第一个元素的双向迭代器。
end()返回指向列表中最后一个元素之后位置的双向迭代器。
rbegin()返回指向列表中最后一个元素的反向迭代器,即正向的 end() 位置。
rend()返回指向列表中第一个元素之前位置的反向迭代器,即正向的 begin() 位置。

演示代码

template <class T>
void Print(const list<T>& tmp) {//auto ch = tmp.begin();auto ch = tmp.begin();while (ch != tmp.end()){cout << *ch << " ";ch++;}for (auto it : tmp){cout << it << " ";}cout << endl;
}

list capacity

函数声明描述
empty()检测 std::list 是否为空。如果列表为空,则返回 true;否则返回 false
size()返回 std::list 中有效节点的个数。

演示代码

void test4() {list<int> a1;a1.push_back(1);a1.push_back(2);a1.push_back(3);if (a1.empty()){cout << "no empty"  << endl;}else{cout << "yes emptyz" << endl;}cout << a1.size() << endl;}

list element access

函数声明描述
front()返回 std::list 的第一个节点中值的引用。
back()

返回 std::list 的最后一个节点中值的引用。

Modifiers:

函数声明描述
push_front(val)在 std::list 的首元素前插入值为 val 的新元素。
pop_front()删除 std::list 中的第一个元素。
push_back(val)在 std::list 的尾部插入值为 val 的新元素。
pop_back()删除 std::list 中的最后一个元素。
insert(position, val)在 std::list 的指定位置 position 插入值为 val 的新元素。
erase(position)删除 std::list 中位于 position 的元素。
swap(list)交换当前 std::list 与另一个 std::list 中的所有元素。
clear()清空 std::list 中的所有有效元素。

这些接口以前都使用过,所以这里就不使用了

list的迭代器失效

vector里面insert和erase都会出现迭代器失效的问题,在list里面insert是不会出现迭代器失效的问题,为什么?因为他们不是在一个内存中操作,反之list的erase会出现迭代器失效的问题,因为当释放一个节点的时候指向那个节点的指针变成了野指针。

这里我们先看一下他的返回值是iterator

这边直接报错了

这个迭代器失效我们该如何去解决?

话可以实现一个删除所有的偶数

template <class T>
void Print(const list<T>& tmp) {//auto ch = tmp.begin();auto ch = tmp.begin();while (ch != tmp.end()){cout << *ch << " ";ch++;}cout << endl;for (auto it : tmp){cout << it << " ";}cout << endl;
}void test5() {int arr[] = { 1,2,3,4,5,6,7 };int n = sizeof(arr) / sizeof(arr[0]);list<int> a1(arr,arr+n);auto it = a1.begin();while (it != a1.end()){if (*it % 2 == 0) {a1.erase(it++);//将这个位置传过去之后}else{it++;}}Print(a1);
}

list模拟实现

只实现一些常用接口

#pragma once
#include <cassert>    // 包含断言头文件
#include <iostream>   // 包含输入输出流头文件
#include <initializer_list> // 包含用于初始化列表的头文件using namespace std;namespace yang {// 定义链表节点结构template<class T>struct list_node {T _data;       // 节点中存储的数据list_node<T>* _next; // 指向下一个节点的指针list_node<T>* _prev; // 指向前一个节点的指针// 构造函数,可选地初始化数据list_node(const T& data = T()): _data(data), _next(nullptr), _prev(nullptr) {}};// 迭代器结构template<class T, class Ref, class Ptr>struct list_iterator {typedef list_node<T> Node; // 节点类型的别名typedef list_iterator<T, Ref, Ptr> Self; // 迭代器类型的别名Node* _node; // 指向当前节点的指针// 构造函数,接受一个节点指针list_iterator(Node* node): _node(node) {}// 解引用运算符,返回数据的引用Ref& operator*() {return _node->_data;}// 成员访问运算符,返回指向数据的指针Ptr* operator->() {return &_node->_data;}// 前缀递增运算符,移动到下一个节点Self& operator++() {_node = _node->_next;return *this;}// 后缀递增运算符,移动到下一个节点,并返回旧值Self operator++(int) {Self tmp(*this);_node = _node->_next;return tmp;}// 前缀递减运算符,移动到前一个节点Self& operator--() {_node = _node->_prev;return *this;}// 后缀递减运算符,移动到前一个节点,并返回旧值Self operator--(int) {Self tmp(*this);_node = _node->_prev;return tmp;}// 等于运算符bool operator==(const Self& s) const {return _node == s._node;}// 不等于运算符bool operator!=(const Self& s) const {return _node != s._node;}};// 链表类定义template <class T>class list {public:typedef list_node<T> Node; // 节点类型的别名typedef list_iterator<T, T&, T*> iterator; // 非常量迭代器的别名typedef list_iterator<T, const T&, const T*> const_iterator; // 常量迭代器的别名// 初始化为空链表void empty_init() {_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}// 默认构造函数list() {empty_init();}// 复制构造函数list(const list<T>& ls) {empty_init();for (auto& it : ls)push_back(it);}// 从初始化列表构造list(initializer_list<T> ls) {empty_init();for (auto& it : ls)push_back(it);}// 析构函数~list() {clear();delete _head;_head = nullptr;}// 交换函数void swap(list<T>& ls) {std::swap(_head, ls._head);std::swap(_size, ls._size);}// 赋值运算符,使用交换惯用法list<T>& operator=(list<T> ls) {swap(ls);return *this;}// 清除所有元素void clear() {auto it = begin();while (it != end()) {it = erase(it);}}// 在链表尾部添加元素void push_back(const T& val) {insert(end(), val);}// 在链表头部添加元素void push_front(const T& val) {insert(begin(), val);}// 在指定位置之前插入值iterator insert(iterator pos, const T& val) {Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(val);cur->_prev = newnode;newnode->_next = cur;prev->_next = newnode;newnode->_prev = prev;++_size;return iterator(newnode); // 返回指向新插入元素的迭代器}// 移除第一个元素void pop_front() {erase(begin());}// 移除最后一个元素void pop_back() {erase(--end());}// 移除指定位置的元素iterator erase(iterator pos) {Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;next->_prev = prev;prev->_next = next;delete pos._node;--_size;return iterator(next); // 返回指向下一个元素的迭代器}// 返回指向链表开始处的迭代器iterator begin() {return iterator(_head->_next);}// 返回指向链表结束处的迭代器iterator end() {return iterator(_head);}// 返回指向链表开始处的常量迭代器const_iterator begin() const {return const_iterator(_head->_next);}// 返回指向链表结束处的常量迭代器const_iterator end() const {return const_iterator(_head);}// 返回链表的大小size_t size() const {return _size;}// 判断链表是否为空bool empty() const {return _size == 0;}private:Node* _head; // 指向头节点的指针size_t _size; // 链表中的元素数量};// 打印容器内元素的函数template <class Container>void Print_t(const Container& tmp) {for (auto ch = tmp.begin(); ch != tmp.end(); ++ch) {std::cout << *ch << " ";}cout << endl;}// 展示使用的函数void func(const list<int>& lt) {Print_t(lt);}// 测试函数void test1() {list<int> lt0({1, 2, 3, 4, 5, 6});list<int> lt1 = {1, 2, 3, 4, 5, 6, 7, 8};const list<int>& lt3 = {1, 2, 3, 4, 5, 6, 7, 8};func(lt0);Print_t(lt1);}
}

解析

以下是您提供的代码的详细解析:

1. 基础结构定义

list_node 结构体

这是链表的基本组成单元,它包含了一个数据成员 _data 和两个指针成员 _next_prev 分别指向链表中的下一个和前一个节点。

template<class T>
struct list_node {T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& data = T()): _data(data), _next(nullptr), _prev(nullptr) {}
};

2. 迭代器定义

list_iterator 结构体

这是一个双向迭代器,它允许你从前向后或从后向前遍历链表。迭代器持有指向当前节点的指针 _node

template<class T, class Ref, class Ptr>
struct list_iterator {// ... iterator implementation ...
};

3. 链表类定义

list 类

这是一个模板类,实现了双向链表的基本功能,包括插入、删除、迭代等操作。

template <class T>
class list {// ... list implementation ...
};

4. list 类的成员函数解析

构造与析构
  • 默认构造函数:创建一个空链表。
  • 复制构造函数:通过拷贝另一个 list 对象来创建一个新的 list
  • 从初始化列表构造:通过给定的初始化列表来创建一个新的 list
  • 析构函数:释放链表中的内存资源。
list() {empty_init();
};list(const list<T>& ls) {empty_init();for (auto& it : ls)push_back(it);
};list(initializer_list<T> ls) {empty_init();for (auto& it : ls)push_back(it);
};~list() {clear();delete _head;_head = nullptr;
};
插入与删除
  • push_back:在链表尾部插入一个新元素。
  • push_front:在链表头部插入一个新元素。
  • insert:在给定迭代器的位置插入一个新元素。
  • pop_front:移除链表的第一个元素。
  • pop_back:移除链表的最后一个元素。
  • erase:移除指定迭代器位置的元素。
void push_back(const T& val) {insert(end(), val);
};void push_front(const T& val) {insert(begin(), val);
};iterator insert(iterator pos, const T& val) {// ... implementation details ...
};void pop_front() {erase(begin());
};void pop_back() {erase(--end());
};iterator erase(iterator pos) {// ... implementation details ...
};
迭代器操作
  • begin:返回指向链表开始位置的迭代器。
  • end:返回指向链表结束位置的迭代器(实际上是最后一个节点之后的位置)。
iterator begin() {return _head->_next;
};iterator end() {return _head;
};const_iterator begin() const {return _head->_next;
};const_iterator end() const {return _head;
};
其他操作
  • clear:移除链表中的所有元素。
  • size:返回链表中的元素个数。
  • empty:判断链表是否为空。
  • swap:交换两个链表的内容。
  • operator=:赋值操作符,使用交换惯用法实现。
void clear() {auto it = begin();while (it != end()) {it = erase(it);}
};size_t size() const {return _size;
};bool empty() const {return _size == 0;
};void swap(list<T>& ls) {// ... implementation details ...
};list<T>& operator=(list<T> ls) {swap(ls);return *this;
};

5. 辅助函数

这是一个通用的打印函数,可以用来打印任何实现了迭代器接口的容器。

template <class Container>
void Print_t(const Container& tmp) {// ... implementation details ...
}

总结

该实现提供了一个简单的双向链表,支持基本的操作,如插入、删除、遍历等。它还包含了一些辅助函数,比如 Print_t 用于打印链表内容,以及一个测试函数 test1 来展示如何使用这些功能。

注意事项

  • 代码中有一些注释掉的部分,例如 push_back 的原始实现,这些部分可以根据需要恢复。
  • 代码中没有实现 find 方法,如果需要查找特定元素,可以考虑实现该方法。
  • 代码中使用了 assert.h,但在实际的链表实现中可能不需要,因为没有使用断言的地方。
  • 可以考虑添加更多的安全检查,例如在删除元素时检查迭代器的有效性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FMR—Feature-metric Registration论文解读

目录 一、导言 二、先导知识 1、逆组合算法 三、相关工作 1、优化算法为主的配准工作 2、基于特征的点云配准 3、端到端学习的配准 四、FMR框架 1、Encoder模块 2、Decoder分支模块 3、特征指标配准分支模块 4、损失函数 五、数据集 1、ModelNet40 2、7Scene数据…

SpringBoot快速入门(自动创建)

目录 前言 步骤 1 创建项目 2 选择生成器springBoot 3 修改后&#xff0c;如图所示 4 点击下一步 5 点击Web----SpringWeb 6 点击创建 6.1 如果发生报错如: 6.2 替换合适版本&#xff0c;等待重新加载 7 添加contronller类 7.1 添加HelloController 类 8 ​​创建…

基于JAVA的医院管理住院系统研究与实现

点击下载源码 基于JAVA的医院管理住院系统研究与实现 摘 要 医院管理住院系统是一项集多类学科为一体的系统&#xff0c;其中包含医学、信息、计算机等学科&#xff0c;广泛的应用在当今欧美等发达国家&#xff0c;给治疗患者们提供了很大的便利。假如全面实现了这一系统&…

【启明智显技术分享】工业级HMI芯片Model3A开发过程中问题记录笔记

一、Model3A芯片介绍 Model3A是启明智显针对工业、行业以及车载产品市场推出的一款高性能、低成本的工业级HMI&#xff08;Human-Machine Interface&#xff0c;人机界面&#xff09;芯片。该芯片主要应用于工业自动化、智能终端HMI、车载仪表盘、两轮车彩屏仪表、串口屏、智能…

Docker容器管理之FAQ

一、前言 某次&#xff0c;某容器服务发现无法使用了&#xff0c;查看状态为restaring状态&#xff0c;后看是云主机重启了&#xff0c;导致本地的nfs-server未自动启动&#xff0c;导致关联的集群主机&#xff0c;远程挂载点无法使用&#xff0c;影响容器服务运行。故此&#…

老师分班查询助手,新学期老师都在用!

作为一名教师&#xff0c;您是否曾经在新学期伊始&#xff0c;面对着一堆学生名单和分班结果&#xff0c;感到无从下手&#xff1f;是否曾经历过在黑板上一笔一划地写下每个学生的名字和班级&#xff0c;然后一遍又一遍地回答家长和学生的询问&#xff1f;这样的场景&#xff0…

web页面的性能测试

背景 测试大模型主要web页面的性能 使用工具 通过google自带的lighthouse测试页面的性能 各个参考指标 First Contentful Paint(FCP):测量在用户导航到页面后浏览器呈现第一段 DOM 内容所花费的时间。页面上的图像、非白色<canvas>元素和 SVG 被视为 DOM 内容&#…

ECMAScript6语法:类

在 ES6 中新增了类的概率&#xff0c;多个具有相同属性和方法的对象就可以抽象为类。类和对象的关系如下&#xff1a; &#xff08;1&#xff09;类抽象了对象的公共部分&#xff0c;它泛指某一大类&#xff08;class&#xff09;。 &#xff08;2&#xff09;对象特指通过类…

Linux:基础IO

目录 1. stdin & stdout & stderr 2. 系统文件I/O 1. 接口介绍 open write read close lseek 2. open函数返回值 3. 文件描述符fd 0 & 1 & 2 文件描述符的分配规则 重回定向 dup2 简易Shell的模拟实现 4. FILE 5. 再谈对文件的理解 1. stdin …

threejs webgl效果 功能特效

雷达效果 ​飘扬的红旗 光柱效果 OD线 下雪 下雨 光墙效果 能源球 烟火效果 threejs烟花效果 光圈效果 threejs 光圈 波动 function initScene() {scene new THREE.Scene();}function initCamera() {camera new THREE.PerspectiveCamera(45, window.innerWidth / window.inne…

深入探索PDF源码解析:从PDF到Excel的数据统计分析找到正文

在数字化时代&#xff0c;数据已成为企业决策和业务运营的关键。PDF文档作为一种广泛使用的文件格式&#xff0c;其中蕴含着大量有价值的信息。然而&#xff0c;PDF文档的结构和格式使得直接对其进行数据提取和分析变得复杂。为了解决这个问题&#xff0c;我们采取了一种创新的…

SQL注入实例(sqli-labs/less-17)

0、初始网页 1、确定闭合字符 注入点在于password框&#xff0c;闭合字符为单引号 2、爆库名 1 and updatexml(1,concat(0x7e,database(),0x7e),1)# 1 and (select 1 from (select count(*),concat((select database()),floor(rand()*2))x from information_schema.tables gr…

经纬恒润亮相第四届焉知汽车年会,功能安全赋能域控

8月初&#xff0c;第四届焉知汽车年会在上海举行。此次年会围绕当下智能电动汽车的热点和焦点&#xff0c;聚焦于智能汽车场景应用、车载通信、激光雷达、智能座舱、功能安全、电驱动系统等多个领域&#xff0c;汇聚了来自OEM、科技公司、零部件供应商、测试认证机构、政府院校…

Spark SQL Catalyst工作流程

我们写的SQL语句&#xff0c;会经过一个优化器 (Catalyst)&#xff0c;转化为 RDD&#xff0c;交给集群执行。 而Catalyst在整个Spark 生态中的地位也是至关重要的。 SQL到RDD中间经过了一个Catalyst&#xff0c;它就是Spark SQL的核心&#xff0c;是针对Spark SQL语句执行过程…

使用pytest+selenium编写网页UI自动化脚本和用例

1 UI自动化测试 UI自动化测试&#xff08;User Interface Automation Testing&#xff09;是一种通过编写脚本或使用自动化测试工具&#xff0c;对界面&#xff08;UI&#xff09;进行自动化测试的方法。原理主要是模拟用户打开客户端或网页的UI界面&#xff0c;自动化执行用户…

kali安装docker

docker 安装 ● 1、更新 kali 下载资料源&#xff1a;apt-get update ● 2、如果出现上面没有数字签名问题&#xff0c;那就是需要下载证书 使用命令&#xff1a; wget archive.kali.org/archive-key.asc #下载证书 apt-key add archive-key.asc #添加证书 ● 3、重新更新一…

redis列表若干记录

2、列表 ziplist ziplist参数 entry结构 entry-data:节点存储的元素prelen&#xff1a;记录前驱节点长度encoding&#xff1a;当前节点编码格式encoding encoding属性 使用多个子节点存储节点元素长度&#xff0c;这种多字节数据存储在计算机内存中或者进行网络传输的时的字节…

redis面试(十六)公平锁释放和排队加锁

锁释放 RedissonFairLock.unlockInnerAsync()方法 这和加锁的逻辑没有太大区别 也就是说在客户端A他释放锁的时候&#xff0c;也会走while true的脚本逻辑&#xff0c;看一下有序集合中的元素的timeout时间如果小于了当前时间&#xff0c;就认为他的那个排队就过期了&#xf…

如何减少 Docker 镜像大小:6 种优化方法

如果您想减少docker镜像的大小&#xff0c;您需要使用构建docker镜像的标准最佳实践。 本博客讨论了您可以快速实施的各种优化技术&#xff0c;以制作最小、最精简的 docker 镜像。我们还将介绍一些用于 Docker 镜像优化的最佳工具。 Docker 作为一种容器引擎&#xff0c;可以…

k8s核心架构分析

k8s核心概念概述 Kubernetes入门&#xff1a;掌握集群核心&#xff0c;释放容器潜能 技术爱好者们&#xff0c;CD集群的核心概念是构建、部署和管理容器化应用的基石。掌握这些概念&#xff0c;不仅助你深入理解技术细节&#xff0c;更能在CD集群中自如操作&#xff0c;无论是…