C++---list常用接口和模拟实现

news2024/12/25 0:43:23

list---模拟实现

  • list的简介
  • list函数的使用
    • 构造函数
    • 迭代器的使用
    • list的capacity
    • list element access
    • list modifiers
  • list的模拟实现
    • 构造函数,拷贝构造函数和=
    • 迭代器
    • begin和end
    • insert和erase
    • clear和析构函数
  • 源码

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
void ListTest2()
{
	std::list<int> a(4, 5);
	std::cout << "a的list" << ':';
	for (auto& e : a)
	{
		std::cout << e << ' ';
	}
	std::cout << std::endl;
	std::list<int> b;//Empty

	std::list<int> c(a.begin(), a.end());
	std::cout << "c的list" << ':';
	for (auto& e : c)
	{
		std::cout << e << ' ';
	}

}

在这里插入图片描述

迭代器的使用

函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置
	std::list<int> a(4, 5);
	std::cout << "a的list" << ':';
	std::list<int>::iterator it = a.begin();
	while (it != a.end())
	{
		std::cout << *it << ' ';
		++it;
	}

在这里插入图片描述

list的capacity

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

list element access

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

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中的有效元素

list的模拟实现

要想模拟实现list,首先我们可以定义一个节点类。为什么要定义一个节点类呢,想想数据结构中的双链表,每个元素都在独立的节点中,通过节点的前驱指针和后继指针指向前后位置。

list的模拟实现跟vector和string实现是不一样的,vector本质上是一个数组,数组本身就是一个迭代器,而list是一个个的节点,通过指针联系在一起,所以vector类不用对迭代器进行封装,可以直接使用,list不一样,节点++是什么是不清楚的,这个时候可以使用运算符重载,对++,–,*等进行重载。

	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;

		list_node(const T& val = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_val(val)
		{}
	};

再定义一个list类,再这个类中,完成其他函数的实现

	template<class T>
	class list
	{
	public:
	private:
		Node* _head;
		size_t _size;
	};

构造函数,拷贝构造函数和=

list的构造函数实现,跟双链表中的初始化是一样的,将next指针和prev指针都指向头节点,形成一个环。

		void EmptyInit()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

拷贝构造函数再拷贝之前,要先开一个头节点的空间,然后再头节点后面依次把值进行尾插即可。

		list(const list<T>& it)
		{
			EmptyInit();

			for (auto& e : it)
			{
				push_back(e);
			}
		}

赋值重载=和vector里面的写法是一样的。

		list<T>& operator=(list<T> it)
		{
			swap(it);

			return *this;
		}

通过传参创建出一个临时变量,然后将其交换。

迭代器

要想实现list的迭代器,需要将原生态指针进行封装。

list是由一个个的节点组成,节点++,解引用,–,等操作是找不到对应的数据的,所以需要用自定义类型对指针进行封装,从而完成一系列操作。

迭代器有一个const类型的迭代器,有一个无const类型的,再实现一个无const类型的迭代器之后,把代码赋值粘贴,也可以改成带const类型的迭代器,但这样显然是代码冗余的,重复的太多,这个时候,那些大佬们就通过增加模板参数,来解决代码冗余,根据参数的类型,实列化出不同的类。

	
//typedef __list_iterator<T, T&, T*> iterator;
//typedef __list_iterator<T,const T&,const T*> const_iterator;
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)
		{}
        
		//对节点解引用,是找不到我们存储的数据的,因为节点里面存的是val,next和prev,重载的目是使其对迭代器解引用可以找到有效数据
		Ref operator*()
		{
			return _node->_val;
		}

		Ptr operator->()
		{
			return &(_node->val);
		}
		
        //指针++,到下一个位置,其实就是让节点向后移动
		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 *this;
		}
		
		bool operator !=(const self& it) const
		{
			return _node != it._node;
		}

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


	};

以上就是用自定义的类,对指针进行封装。

begin和end

对指针进行封装之后,就可以实现begin和end了。

begin只要返回头节点的下一个节点即可

end就是头节点

		iterator begin()
		{
			return _head->_next;
		}

		iterator end()
		{
			return _head;
		}

		const_iterator begin() const
		{
			return _head->_next;
		}

		const_iterator end() const
		{
			return _head;
		}

insert和erase

有一个insert,就可以完成再任意位置插入。

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

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

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			prev->_next = newnode;
			newnode->_next = cur;

			newnode->_prev = prev;
			cur->_prev = newnode;

			return newnode;
		}

先找到当前节点cur和当前节点的上一个节点prev,再new出一个要插入的节点newnode,使得cur和newnode进行双向连接,prev和newnode进行双向连接。


删除一个节点,只需要让当前节点的上一个节点和下一个节点完成双向连接即可。

		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;

			--_size;

			return next;
		}

clear和析构函数

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

			_size = 0;
		}
		
		
		~list()
		{}

源码

#pragma once

#include <assert.h>
#include <algorithm>

namespace HaiFan
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;

		list_node(const T& val = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_val(val)
		{}
	};



	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)
		{}

		T& operator*()
		{
			return _node->_val;
		}

		T* operator->()
		{
			return &(_node->val);
		}

		T& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		T operator++(int)
		{
			self tmp(*this);

			_node = _node->_next;

			return tmp;
		}

		T& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		T operator--(int)
		{
			self tmp(*this);
			_node = _node->prev;

			return *this;
		}

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

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


	};

	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T,const T&,const T*> const_iterator;

		iterator begin()
		{
			return _head->_next;
		}

		iterator end()
		{
			return _head;
		}

		const_iterator begin() const
		{
			return _head->_next;
		}

		const_iterator end() const
		{
			return _head;
		}

		list()
		{
			EmptyInit();
		}

		list(const list<T>& it)
		{
			EmptyInit();

			for (auto& e : it)
			{
				push_back(e);
			}
		}

		list<T>& operator=(list<T> it)
		{
			swap(it);

			return *this;
		}

		void swap(list<T>& it)
		{
			std::swap(_head, it._head);
			std::swap(_size, it._size);
		}

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

			_size = 0;
		}

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

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

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

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

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			prev->_next = newnode;
			newnode->_next = cur;

			newnode->_prev = prev;
			cur->_prev = newnode;

			return newnode;
		}

		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;

			--_size;

			return next;
		}

		size_t size()
		{
			return _size;
		}

		void EmptyInit()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		~list()
		{}

	private:
		Node* _head;
		size_t _size;
	};
}

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

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

相关文章

[用go实现解释器]笔记1-词法分析

本文是《用go实现解释器》的读书笔记 ​ https://malred-blog​malred.github.io/2023/06/03/ji-suan-ji-li-lun-ji-shu-ji/shi-ti/go-compile/yong-go-yu-yan-shi-xian-jie-shi-qi/go-compiler-1/#toc-heading-6http://个人博客该笔记地址 ​github.com/malred/malanghttp:/…

入行软件测试的一些工作感悟

成为xx一员测试已经有1年半了&#xff0c;一直没有真正坐下来花些时间将自己的思路理清一下。刚好近期公司落地了OKR&#xff0c;给自己制定了OKR之后思路终于开始清晰起来&#xff0c;朦朦胧胧地开始看清了远方的路&#xff0c;麻着胆子分析一下自己&#xff0c;毕竟摸黑走路的…

Screens 4 for mac VNC客户端 强大的远程控制工具

Screens 4 for Mac 是一款功能强大的 VNC 客户端软件&#xff0c;为 Mac 用户提供了便捷的远程访问和控制解决方案。无论您是需要远程管理服务器、办公电脑&#xff0c;还是需要远程协助他人解决问题&#xff0c;Screens 4 都是您的理想选择。 Screens 4 for Mac具备简洁直观的…

小白到运维工程师自学之路 第六十二集 (docker持久化与数据卷容器)

一、概述 Docker持久化是指将容器中的数据持久保存在主机上&#xff0c;以便在容器重新启动或迁移时不丢失数据。由于Docker容器是临时和可变的&#xff0c;它们的文件系统默认是易失的&#xff0c;这意味着容器中的任何更改或创建的文件都只存在于此容器的生命周期内。但是&a…

基于低代码平台快速搭建应用

一、前言 近年来&#xff0c;SaaS行业的迅猛发展带动了低代码领域的快速兴起。国外的低代码创业公司如Mendix、Outsystems&#xff0c;以及国内的软件业巨头如华为、用友、金蝶等和小众高性价比的引迈JNPF都已经纷纷涉足低代码市场。根据Transparency Market Research的报告&am…

独立站运营要做哪些工作?包含哪些模块?

在电商行业中&#xff0c;广告投放和设计等岗位的招聘可能会相对容易&#xff0c;但真正理解并有效执行独立站运营的人员却十分稀少。因此&#xff0c;今天将聚焦于独立站的运营&#xff0c;特别是针对精品垂直站和品牌站的运营。 首先&#xff0c;我们需要了解“运营”的含义…

vue2使用v-viewer实现图片预览

v-viewer 用于图片浏览的Vue组件&#xff0c;支持旋转、缩放、翻转等操作&#xff0c;基于viewer.js。 中文文档&#xff1a;Vue图片浏览组件v-viewer&#xff0c;支持旋转、缩放、翻转等操作 | Mirari’s Blog 代码示例&#xff1a;https://mirari.cc/v-viewer/ 在Vue.js 2…

Java并发系列之三:乐观锁机制

上一篇悲观锁中&#xff0c;我们讲到悲观锁的四种状态时&#xff0c;提到了“无锁”这种状态&#xff0c;并解释其有两种语义&#xff0c;一种是对共享资源不进行任何同步原语保护&#xff1b;另一种是共享资源会出现被竞争的情况&#xff0c;但是不使用操作系统同步进行保护&a…

17、Spring6整合JUnit5

目录 17、Spring6整合JUnit5 17.1 Spring对JUnit4的支持 准备工作&#xff1a; 声明Bean spring.xml 单元测试&#xff1a; 17.2 Spring对JUnit5的支持 17、Spring6整合JUnit5 17.1 Spring对JUnit4的支持 准备工作&#xff1a; <?xml version"1.0" enco…

Java课题笔记~ MyBatis映射文件

映射文件是MyBatis中的重要组成部分&#xff0c;它包含了开发中编写的SQL语句、参数、结果集等。映射文件需要通过MyBatis配置文件中的<mapper>元素引入才能生效。MyBatis规定了映射文件的层次结构。 1、映射文件概览 <?xml version"1.0" encoding"…

浅谈document.write()输出样式

浅谈document.write()输出样式 js中的最基本的命令之一&#xff1a;document.write&#xff08;&#xff09;&#xff0c;用于简单的打印内容到页面上&#xff0c;可以逐字打印你需要的内容——document.write("content"),这里content就是需要输出的内容&#xff1b;…

2023 ChinaJoy 圆满闭幕,FairGuard游戏加固亮相 BTOB 展区

提振行业 产业复苏 2023年7月28日至7月31日&#xff0c;第二十届中国国际数码互动娱乐展览会( ChinaJoy)于上海新国际博览中心圆满举办。本届ChinaJoy作为疫情结束后的第一个国际性数字娱乐领域的重要产业盛会&#xff0c;对于提振行业信心、加快产业复苏、增进国际间的交流与…

如何成为linux服务端C++开发专家?

想成为linux服务端C开发专家&#xff0c;只能自己慢慢学&#xff0c;在实践中摸索&#xff0c;我敢说没几个人说自己是linux服务端C开发专家 ! 这里说下鹅厂关于Linux C方向 的使用场景 。 进腾讯最好的方向是 Linux C方向&#xff0c;目前腾讯由于历史原因&#xff0c;还有游…

16、外部配置源与外部配置文件及JSON配置

外部配置源与外部配置文件及JSON配置 application.properties application.yml 这些是配置文件&#xff0c; 命令行配置、环境变量配置、系统属性配置源&#xff0c;这些属于配置源。 外部配置源的作用&#xff1a; Spring Boot相当于对Spring框架进行了封装&#xff0c;Spri…

java环境搭建 Ubuntu Linux

jdk的安装和配置环境变量 使用apt sudo apt install default-jdk若是安装成功了在终端输入java -version来查看是否安装成功 使用官网下载的jdk包 直接在百度上搜索jdk&#xff0c;选择图片这个 网址:jdk下载网址 若是arm就选择带有arm的&#xff0c;反之选择x64的&#…

智能疏散照明控制系统在世博文化中心的应用

安科瑞 华楠 摘要:世博文化中心作为世博园区一轴四馆的核心建筑之一&#xff0c;其建筑面积大&#xff0c;人员密集&#xff0c;人流通道众多&#xff0c;疏散路径复杂。为了确保火灾发生时人员能安全、迅速的疏散&#xff0c;在电气设计中设置了一套智能疏散照明系统。通过介…

wps 预加载项插件本地开发启动项目打开wps 客户端,未看到加载项菜单,

wps 预加载项插件本地开发启动项目打开wps 客户端&#xff0c;未看到加载项菜单&#xff0c;请检查本地c盘安装目录下“jsplugins.xml”信息是否添加成功 如下图 name 插件项目 url 本地插件运行地址及端口 <jsplugins><jspluginonline name"wps-soft-copyright…

什么是软件检测证明材料,如何才能获得软件检测证明材料?

一、什么是软件检测证明材料 软件检测证明材料是指在软件开发和发布过程中&#xff0c;为了证明软件的质量和合法性&#xff0c;进行的一系列检测和测试的结果的集合。它是软件开发者和用户之间信任的桥梁&#xff0c;可以帮助用户了解软件的性能和安全性&#xff0c;让用户放…

docker 哨兵模式和集群模式安装Redis7.0.12

docker 哨兵模式和集群模式安装Redis7.0.12 1.下载镜像 1.1 配置阿里云加速源 墙外能访问https://hub.docker.com/_/redis 的可跳过 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors 登录后选择左侧的镜像工具>镜像加速器&#xff0c;获取加速器地址&#…

企业数字化转型的核心是什么?

如今&#xff0c;各行各业都在布局数字化转型&#xff0c;而对于中国传统制造业来说&#xff0c;数字化转型更是当务之急&#xff0c;但由于制造企业组织、业务、产品和价值链的复杂性&#xff0c;为数字化转型带来诸多障碍。这篇就来重点分享下&#xff0c;制造业如何做好数字…