list 介绍 及 底层

list的相关文档:list - C++ Reference

一、list的介绍及使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。我们库里的list 是一个带头双向循环链表 。 

1.1 list 的构造

1.2. list 迭代器的使用 

此处,大家可以暂时将迭代器理解成一个指针 , 该指针指向 list 中的某个节点 。

 遍历链表 , 与vector和string 不同的是 , list 不支持 [ ] , 仅支持迭代器和范围 for 遍历链表 。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<list>
using namespace std;int main()
{list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);list<int>lt2 = { 1,2,3,4,5 };list<int>::iterator it1 = lt1.begin();while (it1 != lt1.end()){cout << *it1 << " ";++it1;}cout << endl;for (auto& e : lt2){cout << e << " ";}cout << endl;return 0;
}

 1.3 list modifiers

1)emplace_back 和 push_back 都能实现尾插一个数据 , 有什么区别 ? 

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<list>
using namespace std;class Pos
{
public:Pos(int row,int col):_row(row),_col(col){}
private:int _row;int _col;
};int main()
{list<Pos> lt;Pos p1(1, 1);lt.push_back(p1);lt.push_back(Pos(2, 2));lt.push_back({3,3});lt.emplace_back(p1);lt.emplace_back(Pos(2, 2));//lt.emplace_back({ 3,3 });lt.emplace_back(3, 3);return 0;
}

补充 : 单参数的构造函数 , 支持隐式类型转化 

 

 2)删除指定元素

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<list>
using namespace std;int main()
{list<int> lt1 = { 1,2,3,4,5 };for (auto e : lt1){cout << e << " ";}cout << endl;int x;cin >> x;auto it = find(lt1.begin(), lt1.end(), x);if (it != lt1.end()){lt1.erase(it);}for (auto e : lt1){cout << e << " ";}cout << endl;return 0;
}

1.4 Operations

1)merge : 合并

 

 2)unique : 去重  , 前提:有序

3)splice : 把一个链表的值转移到另一个链表

 可以用来实现类似LRU(最近最少使用)的算法:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<list>
using namespace std;//LRU
int main()
{list<int> lt1 = { 1,2,3,4,5 };for (auto e : lt1){cout << e << " ";}cout << endl;int x;while (cin >> x){auto pos = find(lt1.begin(), lt1.end(), x);if (pos != lt1.end()){lt1.splice(lt1.begin(), lt1, pos);}for (auto e : lt1){cout << e << " ";}cout << endl;}return 0;
}

4)sort : 排序

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<list>
using namespace std;int main()
{list<int> lt1 = { 15,2,31,14,15 };for (auto e : lt1){cout << e << " ";}cout << endl;lt1.sort();for (auto e : lt1){cout << e << " ";}cout << endl;greater<int> gt;lt1.sort(gt);for (auto e : lt1){cout << e << " ";}cout << endl;return 0;
}

 为了简化,我们一般按照下面的写法 ,传仿函数 :

 

 为什么库里面有sort , list 还要实现一个sort , 难道不会是多次一举吗?

我们可以看到 , 不是list 不想用 , 是list 根本用不了 。 这就和迭代器的功能分类有关了。

 

 虽然,算法库的sort 是一个模板,但是也很明显的告诉了你 , 传的要是随机迭代器 。

二、list 底层

模板不支持分离成两个文件 , 这里我们仅创建 list.h 

list 的底层很深刻的体现了封装的优点:

  • 三者的角色分工
    • 节点(Node) - 数据的 "容器" : 存储实际的数据,以及连接其他节点的指针(前向和后向指针)
    • 迭代器(Iterator) - 数据的 "导航员" : 提供访问节点数据的统一方式屏蔽底层节点的实现细节
    • list 类 - 整个链表的 "管理者" : 负责整体的创建、销毁、插入、删除等操作,维护链表的完整性

  • 封装的意义

    • 分离关注点

      • 节点只关心数据存储和连接
      • 迭代器只关心如何访问数据
      • list 类只关心整体管理
      • 这样每个部分可以独立修改,不会互相影响
    • 隐藏实现细节
      • 用户不需要知道节点如何定义
      • 不需要知道迭代器如何移动
      • 只需要调用 list 提供的接口即可
    • 统一访问方式​​​​​​​
      • 无论是 list、vector 还是其他容器,都可以用类似的迭代器方式访问
      • 使得算法可以通用(如 sort、find 等)
    • 提高安全性​​​​​​​
      • 防止用户直接操作节点指针导致的错误
      • 迭代器会自动处理边界检查等问题

 

2.1 list 节点类

1. ( 惯例 ) 如果成员变量 和 成员函数 全部是公有的 , 一般用 struct 。既有公有 又有 私有 , 则用class

2. 如果把节点类放公有 , 不是会不安全 ? 能被任意修改 ?

节点类虽然是公开的 , 但是 是隐形的封装 , 对 list 才有用 , 在使用 list 的接口的时候 , 其实我们并不会知道它底层是双向带头链表 , 也不知道成员变量的具体变量名(不同平台的变量名会不同),所以是安全的 , 在list 的增删查改的时候 , 会高频的调用list 的节点,所以直接放成struct 

	//惯例
//全部是公用,一般用structtemplate<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};

 缺省值给匿名对象 , 因为类型是不确定的 , 给字面量 不合适 。

2.2 list 迭代器

1.   原生指针已经不够用了 ;所以list 的迭代器是 类封装指针 , 重载运算符 , 模拟指针行为

2.  使用struct , 因为需要频繁访问内部成员 , 不建议用访问限定符限制 。

3. 容器迭代器设计思路体现了封装 :屏蔽底层实现细节,屏蔽各容器结构差异 , 本质封装底层细节 和 差异 ,提供统一的访问方式 。

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--(){_node = _node->_prev;return *this;}//返回局部对象就不能再使用引用返回了Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};

 

2.3 list 类

1)insert:

返回 iterator ;由于不需要扩容 , 所以不存在迭代器失效的问题

		iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;//prev newnode curnewnode->_prev = prev;newnode->_next = cur;prev->_next = newnode;cur->_prev = newnode;return iterator(newnode);}

复用insert 实现头插和尾插 :

		void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}

2)erase :

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

注意 : 删除唯独不能删除哨兵位的头结点 !!!

	iterator erase(iterator pos){assert(pos != end());Node* del = pos._node;Node* prev = del->_prev;Node* next = del->_next;//prev del nextprev->_next = next;next->_prev = prev;delete del;return iterator(next);}

 复用erase 实现头删 和 尾删 :

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

3) 析构

		~list(){clear();delete _head;_head = nullptr;}void clear(){auto it = begin();while (it != end()){it = erase(it);}}

clear 是清理资源 , 但是结构依旧保持 。 (注意 : list 的erase 会导致迭代器失效的问题 , 需要更新一下迭代器 。 ) 析构 不仅清理资源 , 同时会把头结点给删除了 。

4)拷贝构造 : 

		//拷贝构造//lt2(lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}

5)赋值重载:

//赋值重载
//lt2 = lt3
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}void swap(list<T>& tmp)
{std::swap(_head, tmp._head);
}

6)所有代码:list.h

#pragma once
#include<assert.h>namespace LF
{//惯例
//全部是公用,一般用structtemplate<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& x = T()):_data(x), _prev(nullptr), _next(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--(){_node = _node->_prev;return *this;}//返回局部对象就不能再使用引用返回了Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}};//template<class T>//struct list_const_iterator//{//	typedef list_node<T> Node;//	typedef list_const_iterator<T> Self;//	Node* _node;//	list_const_iterator(Node* node)//		:_node(node)//	{}//	const T& operator*()//	{//		return _node->_data;//	}//	const T* operator->()//	{//		return &_node->_data;//	}//	Self& operator++()//	{//		_node = _node->_next;//		return *this;//	}//	Self& operator--()//	{//		_node = _node->_prev;//		return *this;//	}//	Self operator++(int)//	{//		Self tmp(*this);//		_node = _node->_next;//		return tmp;//	}//	Self operator--(int)//	{//		Self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	bool operator!=(const Self& s)//	{//		return _node != s._node;//	}//};template<class T>class list{typedef list_node<T> Node;public://typedef list_iterator<T> iterator;//typedef list_const_iterator<T> const_iterator;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;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);}void empty_init(){_head = new Node();_head->_prev = _head;_head->_next = _head;}list(){empty_init();}~list(){clear();delete _head;_head = nullptr;}void clear(){auto it = begin();while (it != end()){it = erase(it);}}//n个val构造list(size_t n, const T& val = T()){empty_init();for (size_t i = 0; i < n; i++){push_back(val);}}//拷贝构造//lt2(lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}//赋值重载//lt2 = lt3list<T>& operator=(list<T> lt){swap(lt);return *this;}void swap(list<T>& tmp){std::swap(_head, tmp._head);}//void push_back(const T& x)//{//	Node* new_node = new Node(x);//	Node* tail = _head->_prev;//	new_node->_next = _head;//	new_node->_prev = tail;//	_head->_prev = new_node;//	tail->_next = new_node;//}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;//prev newnode curnewnode->_prev = prev;newnode->_next = cur;prev->_next = newnode;cur->_prev = newnode;return iterator(newnode);}iterator erase(iterator pos){assert(pos != end());Node* del = pos._node;Node* prev = del->_prev;Node* next = del->_next;//prev del nextprev->_next = next;next->_prev = prev;delete del;return iterator(next);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}private:Node* _head;};
}

2.4 list 和 vector的对比

vector 与 list 都是 STL中非常重要的序列式容器 , 由于两个容器的底层结构不同 , 导致其特性以及应用场景不同 , 其主要不同如下 :

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

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

相关文章

HCIP MGRE实验

一、实验要求 1、R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有Ip地址; 2、 R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方&#xff1b; R2与R5之间使用PPP的CHAP认证&#xff0c;R5为主认证方; R3与R5之间使用HDLC封装; 3、R2、R3构建一…

基于PyTorch的多视角二维流场切片三维流场预测模型

基于PyTorch的多视角二维流场切片三维流场预测模型 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c;觉得好请收藏。点击跳转到网站。 1. 引言 计算流体动力学(CFD)在工程设计和科学研究中扮演…

全新轻量化PHP网盘搜索引擎系统源码

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 全新轻量化PHP网盘搜索引擎系统源码 基于PHPMYSQL开发 一、多样筛选功能&#xff1a;网站支持5类筛选功能&#xff0c;包括默认搜索、网盘类型、文件大小、时间排序以及网盘来源&#x…

C study notes[3]

文章目录operatonsloopsreferencesoperatons the fundamental operators such as ,-,* in C language can be simply manipulated. int sum 5 3; // sum 8 int difference 10 - 4; // difference 6 int product 6 * 7; // product 42the operator / was left to in…

练习实践-基础设施-文件共享-windows和linux之间的文件共享-smb服务搭建

参考来源&#xff1a; 在线书籍-linux就该这么学-第12章 安装软件包 配置文件/etc/samba/smb.conf 运维对待配置文件的态度&#xff0c;非必要不增加 安装完毕后打开Samba服务程序的主配置文件&#xff0c;好在参数并不多&#xff0c;只有37行。其中第17&#xff5e;22行代…

常用设计模式系列(十三)—组合模式

常用设计模式系列&#xff08;十三&#xff09;—组合模式 第一节 前言 hello大家好&#xff0c;今年已经过去了一半&#xff0c;年初立下的flag&#xff0c;不知道实现了没有&#xff0c;你的flag改了多少次&#xff1f;无论自己的愿望是否完成&#xff0c;我们都应该怀揣着追…

字节码操作工具——ByteBuddy应用(3)安全检查

一、检测方法名是否符合规范1、代码&#xff08;1&#xff09;MethodLoggerAgentpackage com.example.agent;import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers;import java.lang.instrument.…

NineData 数据库 DevOps 全面支持 GaussDB,国产化管理再升级!

NineData 数据库 DevOps 平台现已全面兼容 GaussDB 全线产品&#xff08;包括 GaussDB 企业级、DWS 数据仓库、openGauss 开源版&#xff09;&#xff0c;实现一站式管理。无论 GaussDB 实例部署在哪个环境&#xff0c;企业所有开发者都可以通过 NineData 统一访问&#xff0c;…

C++ - 模板进阶

一、非类型模板参数模板参数 分为 类型形参与 非类型形参。 类型形参&#xff1a;出现在模板参数列表中&#xff0c;跟在 class 或者 typename 之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数…

【质量管理】软件缺陷管理实施方案(专业版)

引言 方案目标与范围 本方案以CMMI量化管理要求与ISO 9000质量体系为框架,核心目标是通过标准化缺陷管理流程实现缺陷全生命周期可控。具体包括:确保软件缺陷在全生命周期中被及时发现与修复,减少其对软件质量、发布计划及用户体验的负面影响;以“零缺陷”为首要目标,针对…

Elasticsearch 讲解及 Java 应用实战:从入门到落地

在数据量爆炸的今天&#xff0c;传统数据库的查询能力越来越难以满足复杂的检索需求。比如电商平台的商品搜索&#xff0c;需要支持关键词模糊匹配、多条件筛选、热门度排序等功能&#xff0c;这时候 Elasticsearch&#xff08;简称 ES&#xff09;就成了最佳选择。作为一款分布…

docker pull weaviate 国内拉取失败的问题

我是校内网&#xff0c;尝试了 改镜像源 (cooragent) ruiyCJQ:~/sdb/B/cooragent$ sudo vim /etc/docker/daemon.json [sudo] password for ruiy: (cooragent) ruiyCJQ:~/sdb/B/cooragent$ sudo service docker restart (cooragent) ruiyCJQ:~/sdb/B/cooragent$ sudo docke…

Vue项目使用Univer Sheets

Univer Excel主页链接&#xff1a;安装步骤 1. 安装 使用预设模式的包管理器安装 - 预设模式&#xff1a;可以理解为开包即用的模式&#xff0c;省去很多配置&#xff0c;当然自由度不如插件模式 pnpm add univerjs/presets univerjs/preset-sheets-core2. 前端代码 <te…

Python day24

浙大疏锦行 python day24 内容&#xff1a; 元组&#xff1a;类比于列表&#xff0c;不过元组的元素不能被修改&#xff0c;显示也是从[]改为了()&#xff0c;其余操作则是和列表类似&#xff0c;且元组是有序的可迭代对象&#xff1a;即可以使用迭代器访问的对象&#xff0c…

Three.js 动画系统入门:Tween.js 与 AnimationMixer 的使用

引言 动画是 Three.js 中增强 3D 场景动态效果的核心技术&#xff0c;能够为用户带来沉浸式体验。Three.js 支持通过 Tween.js 实现简单的属性动画&#xff0c;以及通过 AnimationMixer 处理复杂的混合动画和骨骼动画。本文将深入探讨如何使用 Tween.js 控制 Object3D 的属性动…

装修进度管理系统功能对比:主流工具9选

本文分享了9款常用的装修进度管理软件&#xff0c;包括&#xff1a;1.Worktile&#xff1b;2.中望软件&#xff1b;3.三维家&#xff1b;4.Procore&#xff1b;5.易达装修管理系统&#xff1b;6.装修管家&#xff1b;7.Zoho Projects&#xff1b;8.中建君联&#xff1b;9.一品装…

深度学习篇---预训练模型

在深度学习中&#xff0c;预训练模型&#xff08;Pretrained Model&#xff09; 是提升开发效率和模型性能的 “利器”。无论是图像识别、自然语言处理还是语音识别&#xff0c;预训练模型都被广泛使用。下面从概念、使用原因、场景、作用等方面详细介绍&#xff0c;并结合 Pyt…

Redis ①⑦-分布式锁

分布式锁 分布式锁是锁的一种&#xff0c;都是为了解决多线程/多进程环境下&#xff0c;对共享资源的访问冲突问题。 不过&#xff0c;像 Java 的 synchronized 或者 C 的 mutex 这种锁&#xff0c;都是进程内的锁&#xff0c;而分布式锁则是跨越进程/机器的锁。也就是可以针对…

OpenCV-图像预处理➀【图像颜色空间转换、灰度化实验、二值化处理、镜像翻转 和 仿射变换】

文章目录先言一、图像颜色空间转换1.RGB颜色空间2.颜色加法3.颜色加权加法4.HSV颜色空间5.图像转换&#xff08;cvtColor()&#xff09;二、灰度实验1.灰度图2.图像灰度化&#xff08;最大值法&#xff09;3.图像灰度化&#xff08;平均值法&#xff09;4.图像灰度化&#xff0…

APP逆向 day9 安卓开发基础

一.前言 app逆向当然要学安卓基础啦&#xff01;今天我们来教安卓基础当然&#xff0c;安卓基础不会教的很多&#xff0c;比java还要少&#xff0c;还是那句话&#xff0c;了解就好。 二.安卓环境搭建 2.1 安卓介绍 如果做安卓开发 需要会java代码安卓SDK(安卓提供的内置…