【C++STL精讲】list的使用教程及其模拟实现

news2025/1/12 23:12:16

在这里插入图片描述

文章目录

  • 💐专栏导读
  • 💐文章导读
  • 🌷list是什么?
  • 🌷list如何使用?
  • 🌷list的模拟实现
    • 🌺定义list类
    • 🌺构造函数
    • 🌺push_back
    • 🌺pop_back
  • 🌷list迭代器
    • 🌺定义list迭代器的类
    • 🌺迭代器运算符重载的实现
  • 🌷list其它接口的实现
    • 🌺迭代器相关函数
    • 🌺insert——插入
    • 🌺erase——删除
    • 🌺其它删除及插入操作
    • 🌺迭代器区间构造
    • 🌺拷贝构造
    • 🌺赋值重载
    • 🌺析构函数
  • 🌷完整源码

💐专栏导读

🌸作者简介:花想云,在读本科生一枚,致力于 C/C++、Linux 学习。

🌸本文收录于 C++系列,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新!

🌸相关专栏推荐:C语言初阶系列C语言进阶系列数据结构与算法

💐文章导读

本章我们将认识与学习list的使用并且参照STL源码来模拟实现list容器,需要读者具有一定的数据结构基础。通过本章的学习,我们将对类和对象模板的运用更加熟练,同时还会实现list的重要角色——迭代器,让我们对迭代器的了解更上一层楼~

在这里插入图片描述

🌷list是什么?

listC++ STL中的一个容器,是一个双向链表,可以存储任意类型的元素。list中的元素可以通过指针在链表中进行高效的插入和删除操作,因此list通常被用于需要频繁进行插入和删除操作的场合。

🌷list如何使用?

  • 使用list前需包含头文件< list >;
#include <list>
  • 🍁定义一个list容器;
	list<int> mylist; // 声明一个空的list
	list<int> mylist2(10); // 声明一个有10个默认值的list
	list<int> mylist3(5, 2); // 声明一个有5个值为2的list
  • 🍁向list中添加元素;
mylist.push_back(1); // 在list的末尾添加元素1
mylist.push_front(2); // 在list的开头添加元素2
mylist.insert(mylist.begin(), 3); // 在指定位置插入元素3
  • 🍁访问list中的元素;
	cout << mylist.front() << endl; // 访问第一个元素
	cout << mylist.back() << endl; // 访问最后一个元素
  • 🍁遍历list中的元素;
for (auto it = mylist.begin(); it != mylist.end(); ++it) {
    cout << *it << " ";
}
  • 🍁删除list中的元素;
mylist.pop_front(); // 删除第一个元素
mylist.pop_back(); // 删除最后一个元素
mylist.erase(mylist.begin()); // 删除指定位置的元素
mylist.clear(); // 删除所有元素

list的常见使用就是这些了,若想查看更多操作,还需借助STL文档来学习。

🌷list的模拟实现

🌺定义list类

为了区别于库中的list类,我们可以在自己的命名空间中定义list类。list的基本成员变量只有一个——list的头结点(head)并且头节点内不存储有效数据

  • 🍁定义结点结构;
namespace hxy
{
	//定义结点结构
	template<class T>
	struct ListNode
	{
		struct ListNode<T>* _next; // 指向下一个结点
		struct ListNode<T>* _prev; // 指向前一个结点
		T _data; // 结点存储的数据

		ListNode(const T& data = T()) // 构造函数 // 申请一个节点 
			:_next(nullptr),
			_prev(nullptr),
			_data(data)
		{}
	};
}
  • 🍁定义list类;
namespace hxy
{
	//定义结点结构
	template<class T>
	struct ListNode
	{
		struct ListNode<T>* _next; // 指向下一个结点
		struct ListNode<T>* _prev; // 指向前一个结点
		T _data; // 结点存储的数据

		ListNode(const T& data = T()) // 构造函数 // 申请一个节点 
			:_next(nullptr),
			_prev(nullptr),
			_data(data)
		{}
	};
	// list类的定义
	template<class T>
	class list
	{
		typedef ListNode<T> node; // 简写
	
	private:
		node* _head;
	}}

🌺构造函数

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

🌺push_back

  • 🍁对list进行尾插元素;
	void push_back(const T& data)
	{
		node* newNode = new node(data); // 申请一个结点
		node* tail = _head->_prev; // list的尾结点就是头结点的前一个

		// 链接结点
		tail->_next = newNode;
		newNode->_prev = tail;
		newNode->_next = _head;
		_head->_prev = newNode;
	}

🌺pop_back

  • 🍁对list进行尾删;
	void pop_back()
	{
		assert(_head->_next != _head);

		node* tail = _head->_prev;
		tail->_prev->_next = _head;
		_head->_prev = tail->_prev;
		
		delete tail; //释放尾结点
	}

🌷list迭代器

迭代器——作为list最精华的部分,有着及其重要的作用,且同样为list模拟实现中最难的一部分。通过本章的内容,我们对于迭代器的理解会更上一层楼!

stringvector的学习中,我们经常使用下标+[]来访问容器的元素,非常方便。但是到了list以后的内容,[]访问元素的方法将不再适用,我们将使用迭代器。

前面的文章中曾经简单的提到过迭代器,描述它为一种像指针一样却又不单纯是指针的东西(stringvector中的迭代器在一些编译器下确实是原始指针)。接下来我们就模拟实现list的迭代器。

🌺定义list迭代器的类

迭代器本质就是对指针的封装模拟指针的行为,所以我们需要通过运算符重载来模拟实现指针的++--以及*->等操作。

	template<class T,class Ref,class Ptr> // 三个参数的含义分别为--数据类型T--T的引用--T的指针
	struct _list_iterator
	{
		typedef ListNode<T> node;
		typedef _list_iterator<T, Ref, Ptr> self;
		
		node* _node; //迭代器的基本成员变量

		_list_iterator(node* node)
			:_node(node)
		{}

		
		self& operator++()
		{
			//...
		}

		self operator++(int)
		{
			//...
		}

		self& operator--()
		{
				//...
		}

		self operator--(int)
		{
			//...
		}

		Ref& operator*()
		{
			//...
		}

		Ptr& operator->()
		{
			//...
		}

		bool operator==(const self& s)
		{
			//...
		}

		bool operator!=(const self& s)
		{
			//...
		}
	}

🌺迭代器运算符重载的实现

	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;
	}

	Ref& operator*()
	{
		return _node->_data;
	}

	Ptr& operator->()
	{
		return &(_node->_data);
	}

	bool operator==(const self& s)
	{
		return _node == s._node;
	}

	bool operator!=(const self& s)
	{
		return _node != s._node;
	}

🌷list其它接口的实现

有了迭代器,我们才能更好的实现多种接口。

🌺迭代器相关函数

	template<class T>
	class list
	{
		typedef ListNode<T> node;
	public:
		typedef _list_iterator<T,T&,T*> iterator; // 类型重命名——普通迭代器
		typedef _list_iterator<T, const T&,const T*> const_iterator; // 类型重命名——const迭代器
		
		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);
		}
		//...
	}

🌺insert——插入

list模拟实现时,主要完成inserterase(删除)函数实现即可,其它的头插、尾删等函数可以复用这两个函数。

	void insert(iterator pos,const T& data)
	{
		node* newNode = new node(data);

		node* cur = pos._node;
		node* prev = cur->_prev;

		newNode->_prev = prev;
		prev->_next = newNode;
		newNode->_next = cur;
		cur->_prev=newNode;
	}

🌺erase——删除

	iterator erase(iterator pos)
	{
		assert(pos != end());
		node* prev = pos._node->_prev;
		node * next = pos._node->_next;

		prev->_next = next;
		next->_prev = prev;
		delete pos._node;

		return iterator(next); // 返回删除位置的下一个迭代器位置
	}

🌺其它删除及插入操作

	void push_back(const T& data)
	{
		insert(end(),data);
	}

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

	void push_front(const T& data)
	{
		insert(begin(), data);
	}

	void pop_front()
	{
		erase(begin());
	}
	
	void clean() // 清空list
	{
		iterator it = begin();
		while (it != end())
		{
			//it = erase(it);
			erase(it++);
		}
	}

🌺迭代器区间构造

	// 迭代器区间构造
	template<class Iterator>
	list(Iterator begin, Iterator end)
	{
		//初始化
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;

		while (begin != end)
		{
			push_back(*begin);
			++begin;
		}
	}

🌺拷贝构造

	//拷贝构造
	list(const list<T>& lt)
	{
		//初始化
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;

		//复用区间构造
		list<T> tmp(lt.begin(), lt.end());
		swap(tmp);
	}

🌺赋值重载

	//赋值重载
	list<T>& operator=(list<T> lt)
	{
		swap(lt);
		return *this;
	}

🌺析构函数

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

🌷完整源码

#include<iostream>
#include<assert.h>
using namespace std;
namespace hxy
{
	//定义结点结构
	template<class T>
	struct ListNode
	{
		struct ListNode<T>* _next; // 指向下一个结点
		struct ListNode<T>* _prev; // 指向前一个结点
		T _data; // 结点存储的数据

		ListNode(const T& data = T()) // 构造函数 // 申请一个节点 
			:_next(nullptr),
			_prev(nullptr),
			_data(data)
		{}
	};
	//迭代器的定义
	template<class T, class Ref, class Ptr> // 三个参数的含义分别为--数据类型T--T的引用--T的指针
	struct _list_iterator
	{
		typedef ListNode<T> node;
		typedef _list_iterator<T, Ref, Ptr> self;
		node* _node;

		_list_iterator(node* node)
			:_node(node)
		{}

		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;
		}

		Ref& operator*()
		{
			return _node->_data;
		}

		Ptr& operator->()
		{
			return &(_node->_data);
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
	};


	// list类的定义
	template<class T>
	class list
	{
		typedef ListNode<T> node;
	public:
		typedef _list_iterator<T, T&, T*> iterator;
		typedef _list_iterator<T, const T&, const T*> const_iterator;

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

		// 迭代器区间构造
		template<class Iterator>
		list(Iterator begin, Iterator end)
		{
			//初始化
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			while (begin != end)
			{
				push_back(*begin);
				++begin;
			}
		}
		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}
		//拷贝构造
		list(const list<T>& lt)
		{
			//初始化
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			//复用区间构造
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		//赋值重载
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

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

		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 push_back(const T& data)
		{
			insert(end(),data);
		}

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

		void push_front(const T& data)
		{
			insert(begin(), data);
		}

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

		void insert(iterator pos, const T& data)
		{
			node* newNode = new node(data);

			node* cur = pos._node;
			node* prev = cur->_prev;

			newNode->_prev = prev;
			prev->_next = newNode;
			newNode->_next = cur;
			cur->_prev = newNode;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* prev = pos._node->_prev;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

			return iterator(next); // 返回删除位置的下一个迭代器位置
		}

		void clean()
		{
			iterator it = begin();
			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}
	private:
		node* _head;
	};
}

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

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

相关文章

利用STM32实现自平衡机器人功能与方法

将机器人整体开源&#xff0c;同时总结一下机器人搭建过程中遇到的坑和未来的改进方向。在分享的文件里包含了结构设计、程序控制、电路设计以及其他模块相关资料供大家参考。 第一&#xff1a;机器人原理分析 首先来看成品图&#xff1a; 如图所示&#xff0c;该机器人根据陀…

宝塔面板设置虚拟内存Swap降低的方法

宝塔面板可以通过设置Swap/虚拟内存的方式来降低内存使用率和负载&#xff0c;使用宝塔面板的Linux工具箱即可设置虚拟内存&#xff0c;新手站长来详细说下宝塔Linux面板设置Swap/虚拟内存的方法&#xff1a; 宝塔面板设置虚拟内存 设置虚拟内存是通过Linux工具箱&#xff0c…

4.0、Java继承与多态 - 抽象类与抽象方法

4.0、Java继承与多态 - 抽象类与抽象方法 先给大家举个例子 -> 创建一个父类 - 图形类&#xff1b;图形类中有一个计算面积的方法 calculateArea()&#xff1b; 创建三个子类 - 正方形、三角形、圆形 类&#xff1b; 由于我们图形类父类中未明确指明是什么图形&#xff0c…

内网域环境搭建学习

建立的关系就是这样&#xff0c;接下来就开始讲解遇到的困难 虚拟机中我们可以克隆来实现域的搭建 可能会出现这样的问题&#xff0c;原因是你的虚拟机没有关闭&#xff0c;所以才会导致这样的原因&#xff0c;解决方法 将虚拟机打开后&#xff0c;电源 -> 关闭客户机&…

MySQL学习笔记第二天

第03章 基本的SELECT语句 1.SQL概述 1.1 SQL背景知识 1946年&#xff0c;世界上第一台电脑诞生&#xff0c;如今&#xff0c;借由这台电脑发展起来的互联网已经自成江湖。在这几十年里&#xff0c;无数的技术、产业在这片江湖里沉浮&#xff0c;有的方兴未艾&#xff0c;有的…

reactxp搭建,start:windows运行不起来

1、官网 reactxp 2、VSCode和Visual Studio2019 安装VSCode Visual Studio 下载地址 先不用勾选工作负荷&#xff0c;直接安装 3、安装nvm 访问下载地址下载安装nvm&#xff1a; 百度云分享 官网直装链接 nvm的github发行界面下载nvm-setup.exe GitCode镜像下载nvm-setup…

ACL配置学习(附练习题)------ensp

从此文了解ACL配置&#xff0c;欢迎学习、指导。 目录 基本ACL配置举例 高级ACL配置举例 ACL配置练习题 定义 访问控制列表ACL&#xff08;Access Control List&#xff09;本质上是一种报文过滤器。 范围: OSI七层模型中的网络层、传输层的信息。 滤芯&#xff1a;五…

2023年4月实时获取地图边界数据方法,省市区县街道多级联动【附实时geoJson数据下载】

首先&#xff0c;来看下效果图 在线体验地址&#xff1a;https://geojson.hxkj.vip&#xff0c;并提供实时geoJson数据文件下载 可下载的数据包含省级geojson行政边界数据、市级geojson行政边界数据、区/县级geojson行政边界数据、省市区县街道行政编码四级联动数据&#xff0…

大型医院体检管理系统源码,PEIS体检系统源码 丰富的诊断模板,自动产生小结、综述和建议

PEIS体检管理系统源码 体检条码化管理&#xff0c;体检数据比对&#xff0c;丰富的诊断模板&#xff0c;自动产生小结、综述和建议。 文末获取联系 PEIS体检管理系统对医院体检中心进行系统化和规范化的管理&#xff0c;大大提高体检中心的综合管理水平、工作效率。系统从业务…

设计模式-结构型模式之享元模式

5. 享元模式 5.1. 模式动机 面向对象技术可以很好地解决一些灵活性或可扩展性问题&#xff0c;但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时&#xff0c;将导致运行代价过高&#xff0c;带来性能下降等问题。 享元模式正是为解决这一类问题而诞生的。享元模…

GoF代理模式

在java中代理模式的作用: 1.一个对象需要受到保护的时候&#xff0c;可以考虑使用代理对象取完成某个行为. 2.需要给某个对象的功能进行功能增强的时候&#xff0c;可以考虑找一个代理进行增强 3.A对象无法和B对象无法直接沟通&#xff0c;也可以使用代理模式解决 代理模式有三…

WPF mvvm框架Stylet使用教程-窗体交互用法

窗体操作 打开窗体 在stylet框架中&#xff0c;要打开一个窗口或者对话框&#xff0c;只需要直接使用窗口管理器 在要使用的ViewModel中注入IWindowManager&#xff0c;然后使用他的方法操作窗口。 ShowDialog(object viewModel)模态显示ShowWindow(object viewModel) 非模…

修改OPNET帮助文档的默认打开浏览器 给Edge浏览器配置IE Tab插件

我在使用 OPENT Modeler 软件时经常会用到帮助文档&#xff0c;但是其默认打开的是 IE 浏览器&#xff0c;想要其在 Edge 浏览器中打开&#xff0c;但是会出现网页无法打开的情况&#xff0c;这时需要给 Edge 浏览器安装一个 IE Tab 插件。 IE Tab 插件是专门针对浏览器而开发的…

vue3的介绍和两种创建方式(cli和vite)

目录 一、vue3的介绍 &#xff08;一&#xff09;vue3的简介 &#xff08;二&#xff09;vue3对比vue2带来的性能提升 二、vue3的两种创建方式 方式一&#xff1a;使用vue-cli创建&#xff08;推荐--全面&#xff09; 操作步骤 方式二&#xff1a;使用vite创建 操作步…

Spring是什么?关于Spring家族

初识Spring 什么是Spring&#xff1f; Spring是一个开源的Java企业级应用程序开发框架&#xff0c;由Rod Johnson于2003年创建&#xff0c;并在接下来的几年里得到了广泛的发展和应用。它提供了一系列面向对象的编程和配置模型&#xff0c;支持开发各种类型的应用程序&#x…

黑客网站攻击的主要手段

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 黑客与白帽子 有的童鞋觉得黑客和白帽子是同一回事儿&#xff0c;其实不然&#xff1b;而且&#xff0c;他们的工作方式与目标也有很大的差异。 黑客是指一群专门使用计算机…

9.2 变量的指针和指向变量的指针变量

9.2 变量的指针和指向变量的指针变量 一.指针变量的定义二.指针变量的引用三.整理至https://appd872nnyh9503.pc.xiaoe-tech.com/index的笔记 一.指针变量的定义 变量的指针 就是变量的地址。 我们可以定义一个指向 变量 的指针变量。 这种指针变量&#xff0c;我们在定义的时…

信创办公–基于WPS的EXCEL最佳实践系列 (筛选重要数据)

信创办公–基于WPS的EXCEL最佳实践系列 &#xff08;筛选重要数据&#xff09; 目录 应用背景操作步骤1、筛选2、高级筛选 应用背景 在WPS里&#xff0c;筛选有两种&#xff0c;一种是筛选&#xff0c;另外一种则是高级筛选。 操作步骤 1、筛选 可以根据学号、准考证号、考…

MyBatisPlus基础入门学习

系列文章目录 MyBatisPlus基础入门学习 文章目录 系列文章目录前言一、MyBatisPlus简介1.入门案例2.MyBatisPlus概述 二、标准数据层开发1.标准数据层CRUD功能2.分页功能 三、DQL控制1.条件查询方式2.查询投影3.查询条件设定4.字段映射与表名映射 四、DML控制1.Insert2.Delete…

原创文章生成器在线版-ai写作生成器

随着人工智能技术的迅猛发展&#xff0c;越来越多的人开始意识到&#xff0c;利用AI可以实现许多以前不可能想象的事情。其中&#xff0c;一种最能体现人工智能技术优势的应用就是“ai原创文章生成器”。它可以为营销从业者提供一种全新的营销推广方式。 那么&#xff0c;什么是…