【C++】STL——list的模拟实现、构造函数、迭代器类的实现、运算符重载、增删查改

news2025/1/16 16:58:45

文章目录

  • 1.模拟实现list
    • 1.1构造函数
    • 1.2迭代器类的实现
    • 1.3运算符重载
    • 1.4增删查改

1.模拟实现list

list使用文章

在这里插入图片描述

1.1构造函数

在这里插入图片描述

析构函数

在这里插入图片描述

  在定义了一个类模板list时。我们让该类模板包含了一个内部结构体_list_node,用于表示链表的节点。该结构体包含了指向前一个节点和后一个节点的指针以及节点的值。在list中保存了链表的头节点指针和链表长度大小。

namespace my_list
{	
	//用类模板定义一个list结点
	template<class T>
	struct _list_node
	{
		//list结点的构造函数,T()作为缺省值,当未转参时调用
		_list_node(const T& val = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _val(val)
		{}

		//list_node的成员函数,list为带头双向循环链表
		_list_node* _prev;
		_list_node* _next;
		T _val;
	};
	
	//用类模拟实现list
	template<class T>
	class list
	{
	private:
		typedef _list_node<T> Node;
		
	public:
		//list的构造函数,初始化头节点指针
		list()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;

			_size = 0;
		}

		//析构函数
		~list()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}

			_size = 0;

			delete _head;
			_head = nullptr;
		}

	private:
		//成员函数为list的头节点指针,和链表长度
		Node* _head;
		size_t _size;
	};
}

1.2迭代器类的实现

在这里插入图片描述
在这里插入图片描述

  因为list的底层实现和之前的vector和string不同,vector和string是数组和顺序表,而list底层是链表。而对于数组和顺序表,我们可以直接使用指针来实现其迭代器,但是对于list类型就无法使用指针来实现它的迭代器,因为迭代器++无法访问到他的下一个节点。所以我们这里建立一个迭代器的结构体,用来封装node指针,来实现list的专属迭代器

  这段代码定义了一个名为__list_iterator的迭代器结构体,用于遍历链表。该结构体模板有三个模板参数:T表示链表中节点的数据类型,Ref表示引用类型T&,Ptr表示指针类型 T* _node:指向链表节点的指针。构造函数__list_iterator(Node* node):用于初始化迭代器对象,将指针node赋值给_node。通过_node就可以实现各种操作符重载。

  定义了一个迭代器结构体,用于在链表中进行遍历和操作。通过重载运算符,可以使用迭代器对象像指针一样访问链表中的元素。

//使用类进行封装Node*,来实现list的专属迭代器
//list迭代器和一些内置类型迭代器不一样,++无法准确访问到下一个位置
//typedef __list_iterator<T, T&> iterator;
//typedef __list_iterator<T, const T&> 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)
	{}

	//重载*
	Ref operator*()
	{
		return _node->_val;
	}

	//重载->
	Ptr operator->()
	{
		return &_node->_val;
	}

	//重载前置++
	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	//重载后置++
	self operator++(int)
	{
		__list_iterator<T> 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& it) const
	{
		return _node != it._node;
	}

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

  通过建立一个新的结构体来重载迭代器,我们即可实现list专属迭代器对其链表进行++、- -等操作。

typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;

//list迭代器实现
iterator begin()
{
	//隐式类型转换,内置类型转换为自定义类型,实现迭代器功能
	//return _head->_next;
	return iterator(_head->_next);
}

iterator end()
{
	//return _head;
	return iterator(_head);
}

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

const_iterator end() const
{
	//return _head;
	return const_iterator(_head);
}

1.3运算符重载

  迭代器的运算符重载在上面的代码中,已经都基本实现了。

在这里插入图片描述

  赋值运算符重载函数operator=首先创建了一个临时的链表对象lt,并将传入的链表对象的副本赋值给lt。然后,调用swap函数,将当前链表对象和临时链表对象进行交换。这样做的原因是为了实现异常安全的赋值操作。通过将传入的链表对象的副本赋值给临时链表对象,可以确保在交换过程中不会影响到传入的链表对象。 最后,返回当前链表对象的引用。

  通过这样的实现逻辑,可以实现链表对象之间的赋值操作,并且保证在异常发生时不会破坏数据的完整性。

//交换
void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
	std::swap(_size, lt._size);
}

//赋值运算符重载
list<T>& operator=(list<T> lt)
	//list& operator=(list lt)
{
	swap(lt);

	return *this;
}

1.4增删查改

push_back

在这里插入图片描述
  和带头双向循环链表的尾插一样。首先,创建一个新的链表节点newnode,节点的值为传入的参数x。然后,获取到链表的尾指针tail,即头节点的前一个节点。

  接着,将新节点newnode插入到链表的尾部。首先,将新节点的前驱指针指向尾节点tail,将尾节点的后继指针指向新节点。这样就将新节点连接到了链表的尾部。

  然后,将新节点的后继指针指向头节点,将头节点的前驱指针指向新节点。这样就将新节点连接到了链表的头部。

//尾插
void push_back(const T& x)
{
	//创建一个新的链表结点,且获取到链表的尾指针
	Node* newnode = new Node(x);
	Node* tail = _head->_prev;

	//连接尾结点
	newnode->_prev = tail;
	tail->_next = newnode;

	//连接头节点
	_head->_prev = newnode;
	newnode->_next = _head;

	++_size;
}

insert

在这里插入图片描述

  和上面的逻辑类似。

//在pos位置之前插入
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;

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

	++_size;

	return newnode;
}

erase

在这里插入图片描述
  首先,通过断言判断传入的迭代器pos不等于链表的末尾迭代器。如果等于末尾迭代器,表示要删除的节点不存在,会导致错误。然后,根据传入的迭代器pos获取到要删除的节点cur,以及其前驱节点prev和后继节点next。

  接着,将前驱节点的后继指针指向后继节点,将后继节点的前驱指针指向前驱节点。这样就将要删除的节点从链表中断开。然后,释放要删除的节点的内存。还要将链表的大小减1。

  为了避免迭代器失效,返回下一个节点的指针作为新的迭代器。这样可以保证在删除节点后,仍然可以使用返回的迭代器进行遍历和操作。

//删除
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;
}


完整实现

#pragma once

#include<assert.h>

namespace my_list
{	
	//用类模板定义一个list结点
	template<class T>
	struct _list_node
	{
		//list结点的构造函数,T()作为缺省值,当未转参时调用
		_list_node(const T& val = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _val(val)
		{}

		//list_node的成员函数,list为带头双向循环链表
		_list_node* _prev;
		_list_node* _next;
		T _val;
	};

	//使用类进行封装Node*,来实现list的专属迭代器
	//list迭代器和一些内置类型迭代器不一样,++无法准确访问到下一个位置
	//typedef __list_iterator<T, T&> iterator;
	//typedef __list_iterator<T, const T&> 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)
		{}

		//重载*
		Ref operator*()
		{
			return _node->_val;
		}

		//重载->
		Ptr operator->()
		{
			return &_node->_val;
		}

		//重载前置++
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		//重载后置++
		self operator++(int)
		{
			__list_iterator<T> 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& it) const
		{
			return _node != it._node;
		}

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

	//实现const类型迭代器
	/*template<class T>
	struct __list_const_iterator
	{
		typedef list_node<T> Node;
		Node* _node;

		__list_const_iterator(Node* node)
			:_node(node)
		{}

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

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

		__list_const_iterator<T> operator++(int)
		{
			__list_const_iterator<T> tmp(*this);

			_node = _node->_next;

			return tmp;
		}

		bool operator!=(const __list_const_iterator<T>& it)
		{
			return _node != it._node;
		}

		bool operator==(const __list_const_iterator<T>& it)
		{
			return _node == it._node;
		}
	};*/

	//用类模拟实现list
	template<class T>
	class list
	{
	private:
		typedef _list_node<T> Node;
		
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

		//list迭代器实现
		iterator begin()
		{
			//隐式类型转换,内置类型转换为自定义类型,实现迭代器功能
			//return _head->_next;
			return iterator(_head->_next);
		}

		iterator end()
		{
			//return _head;
			return iterator(_head);
		}

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

		const_iterator end() const
		{
			//return _head;
			return const_iterator(_head);
		}

		//初始化
		void empty_init()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;

			_size = 0;
		}

		//list的构造函数,初始化头节点指针
		list()
		{
			empty_init();
		}

		//拷贝构造
		list(const list<T>& lt)
			//list(const list& lt)
		{
			empty_init();

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

		//交换
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		//赋值运算符重载
		list<T>& operator=(list<T> lt)
			//list& operator=(list lt)
		{
			swap(lt);

			return *this;
		}

		//析构函数
		~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}

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

			_size = 0;
		}

		//尾插
		void push_back(const T& x)
		{
			//创建一个新的链表结点,且获取到链表的尾指针
			Node* newnode = new Node(x);
			Node* tail = _head->_prev;

			//连接尾结点
			newnode->_prev = tail;
			tail->_next = newnode;

			//连接头节点
			_head->_prev = newnode;
			newnode->_next = _head;

			++_size;

			//insert(end(), x);
		}

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

		//尾删
		void pop_back()
		{
			erase(--end());
		}

		//头删
		void pop_front()
		{
			erase(begin());
		}

		//在pos位置之前插入
		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;

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

			++_size;

			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
		size_t size()
		{
			/*size_t sz = 0;
			iterator it = begin();
			while (it != end())
			{
				++sz;
				++it;
			}

			return sz;*/

			return _size;
		}

	private:
		//成员函数为list的头节点指针,和链表长度
		Node* _head;
		size_t _size;
	};

	//打印函数
	void print(const list<int>& lt)
	{
		list<int>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			// (*it) += 1;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}


测试代码

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<list>
using namespace std;

#include"list.h"

void test_list1()
{
	//list<int> lt;
	//lt.push_back(1);
	//lt.push_back(2);
	//lt.push_back(3);
	//lt.push_back(4);
	//for (auto e : lt)
	//{
	//	cout << e << " ";
	//}
	//cout << endl;

	my_list::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	
	my_list::list<int>::iterator it = lt.begin();

	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	auto itt = lt.begin();

	while (itt != lt.end())
	{
		++(*itt);
		++itt;
	}

	for (auto e: lt)
	{
		cout << e << " ";
	}
	cout << endl;
}

struct A
{
	A(int a1 = 0, int a2 = 0)
		:_a1(a1)
		, _a2(a2)
	{}

	int _a1;
	int _a2;
};

void test_list2()
{
	my_list::list<A> lt;
	lt.push_back(A(1, 1));
	lt.push_back(A(2, 2));
	lt.push_back(A(3, 3));
	lt.push_back(A(4, 4));

	auto it = lt.begin();
	while (it != lt.end())
	{
		//cout << *(it)._a1 << " " << *(it)._a2 <<endl;
		cout << it->_a1 << " " << it->_a2 << endl;
		++it;
	}
	cout << endl;
}

void test_list3()
{
	my_list::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.push_front(5);
	lt.push_front(6);

	print(lt);

	lt.pop_front();
	lt.pop_back();

	print(lt);

	lt.clear();
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	lt.push_back(40);

	print(lt);

	cout << lt.size() << endl;
	
}


int main()
{
	//test_list1();
	//test_list2();
	test_list3();
	return 0;
}

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

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

相关文章

构建容错软件系统的艺术

现在&#xff0c;我们生活在由软件系统推动的世界中。这些系统深入到我们日常生活的各个方面&#xff0c;其连续、可靠的性能不再是奢侈&#xff0c;而是必需。现在&#xff0c;企业比以往任何时候都更需要保证他们的系统可用、可靠&#xff0c;并具有弹性。这种需求由满足客户…

使用Freemarker模版导出xls文件使用excel打开提示文件损坏

本文是通过一步步的还原事件的发生并解决的一个过程记录&#xff0c;如果想知道如何解决的可以直接跳转文章末尾结论部分 提示一下&#xff0c;关注一下 Table 标签中的 ss:ExpandedRowCount 属性 解决的问题 在项目中使用freemarker的xml模板导出xls格式的Excel文件时&#xf…

哪些行业需要连接云专线?

在诸多行业之中&#xff0c;有一些行业对数据安全性要求高、业务需要实时性、业务需求复杂&#xff0c;往往需要建立起私密、高速、安全的传输通道&#xff0c;云专线是他们经常采用的方案。具体来讲&#xff0c;都有哪些行业需要连接云专线呢&#xff1f;请见下方。 1、金融行…

分布式应用:GFS分布式文件系统

目录 一、理论 1.GlusterFS 2.GlusterFS卷类型 3. GlusterFS与RAID汇总 4.部署 GlusterFS 群集 5.部署Gluster集群环境&#xff08;所有node节点操作&#xff09; 6.创建卷 7. 部署 Gluster 客户端 8.测试 Gluster 文件系统 9.其他的维护命令 二、实验 1.部署Glust…

java学习路程之篇七、进阶知识、时间类、JDK8前后的时间类差异

文章目录 1、时间类2、JDK8(-)时间类3、JDK8()时间类 1、时间类 2、JDK8(-)时间类 3、JDK8()时间类

我的 365 天创作纪念日

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

华为HCIP第三节-----------------------------MPLS VPN

一、MPLS VPN原理与配置 1、MPLS VPN定义 BGP/MPLS IP VPN网络一般由运营商搭建&#xff0c;VPN用户购买VPN服务来实现用户网络之间的路由传递、数据互通等。 MPLS VPN使用BGP在运营商骨干网&#xff08;IP网络&#xff09;上发布VPN路由&#xff0c;使用MPLS在运营商骨干网上…

Vue + ElementUI 实现可编辑表格及校验

效果 完整代码见文末 实现思路 使用两个表单分别用于实现修改和新增处理。 通过一个editIndex变量判断是否是编辑状态来决定是否展示输入框&#xff0c;当点击指定行的修改后进行设置即可&#xff1a; <el-table-columnv-for"(column, index) in columns":key&qu…

操作系统专栏2-文件系统from小林coding

文件系统 文件系统构成虚拟文件系统文件的使用文件的存储连续存储非连续空间存放方式链表方式索引方式 Linux文件的实现方式 空闲分区的管理文件系统结构目录的存储软链接和硬链接 文件系统构成 Linux的设计哲学有一点很重要:一切皆文件,不仅仅是普通的文件和目录,就连块设备,…

数据可视化与机器学习建模:心力衰竭预测_企业科研_论文科研_毕业设计

数据分析与可视化 心力衰竭或心血管疾病 (CVD) 是全球第一大死因&#xff0c;每年夺去大约1790 万人的生命&#xff0c;占全球所有死亡人数的 31%。 大多数心血管疾病可以通过使用全民策略解决烟草使用、不健康饮食和肥胖、缺乏身体活动和有害使用酒精等行为风险因素来预防…

【云原生】k8s组件架构介绍与K8s最新版部署

个人主页&#xff1a;征服bug-CSDN博客 kubernetes专栏&#xff1a;kubernetes_征服bug的博客-CSDN博客 目录 1 集群组件 1.1 控制平面组件&#xff08;Control Plane Components&#xff09; 1.2 Node 组件 1.3 插件 (Addons) 2 集群架构详细 3 集群搭建[重点] 3.1 mi…

Spring入门-技术简介、IOC技术、Bean、DI

前言 Spring是一个开源的项目&#xff0c;并不是单单的一个技术&#xff0c;发展至今已形成一种开发生态圈。也就是说我们可以完全使用Spring技术完成整个项目的构建、设计与开发。Spring是一个基于IOC和AOP的架构多层j2ee系统的架构。 SpringFramework&#xff1a;Spring框架…

GridFsTemplate实现文件管理

目录 需求分析解决方案实现步骤添加maven`spring-boot-starter-data-mongodb`配置文件`GridFsTemplate`实现临时文件和正式文件存储桶分离创建`GridFsTemplateUtil`工具类管理文件的上传、下载和删除创建`GridFsTemplateFileController`控制器暴露上传、下载和删除接口总结需求…

学习gRPC(一)

gRPC 简介 根据官网的介绍&#xff0c;gRPC 是开源高性能远程过程调用&#xff08;RPC&#xff09;框架&#xff0c;可以在任何环境中运行。它可以有效地连接数据中心内部和数据中心之间的服务&#xff0c;并为负载平衡、跟踪、运行状况检查和身份验证提供支持。同时由于其建立…

SAP RSA与RBA,业务需求与技术实现间的桥梁

1. 背景 SAP相关的缩略语很多&#xff0c;抛开背景说RSA与RBA&#xff0c;一定会让大家产生无数的联想。今年介绍的RSA与RBA是SAP最新企业架构方法论中的两个概念&#xff0c;听起来高大上&#xff0c;但其实其初衷和想法都是很容易理解的。话不多说&#xff0c;开始。 2. SA…

C++设计模式之访问者模式

C访问者设计模式 文章目录 C访问者设计模式什么是设计模式什么是访问者设计模式该模式有什么优缺点优点缺点 如何使用 什么是设计模式 设计模式是一种通用的解决方案&#xff0c;用于解决特定的一类问题。它是一种经过验证的代码组织方式&#xff0c;可以帮助开发人员更快地实…

全网最强大的工具箱—utools介绍及分享

今天来介绍一个相见恨晚的PC端工具——utools&#xff0c;什么是utools&#xff1f;用其自身的话来说&#xff1a;“uTools是一个极简、插件化、跨平台的现代化桌面软件。通过自由选配丰富的插件&#xff0c;打造你得心应手的工具集合。”,体验了下&#xff0c;好用且强大&…

vi编辑器的使用二

vi光标命令 vi的查找命令 /string 查找字符串 n 继续查找N 反向继续查找支持正则表达式比如&#xff1a;/^the /end$ vi替换命令 利用:s命令可以实现字符串的替换usage&#xff1a;范围 s/old str/new str (s代表替换。没有指定范围则默认指的是当前行) : s/str1/str2/: s/…

装修小程序,开启装修公司智能化服务的新时代

随着数字化时代的来临&#xff0c;装修小程序成为提升服务质量和效率的关键工具。装修小程序旨在为装修公司提供数字化赋能、提高客户满意度的智慧装修平台。通过装修小程序&#xff0c;装修公司能够与客户进行在线沟通、展示设计方案、提高服务满意度等操作。 装修小程序的好处…

抄写Linux源码(Day2:构建调试环境)

我们计划把操作系统运行在 qemu-system-x86_64 上&#xff0c;使用 gdb 调试 经过 RTFM&#xff0c;可以使用 qemu-system-x86_64 -s -S 让 qemu 在启动之后停住 接着在另一个窗口运行 gdb&#xff0c;输入命令 target remote localhost:1234&#xff0c;即可连接qemu并调试运…