C++ list模拟实现

news2024/11/25 7:15:44

list模拟实现代码:

namespace djx
{
	template<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++(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)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._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;
		}

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

		list()
		{
			empty_init();
		}

		list(const list<T>& 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)
		{
			swap(lt);
			return *this;
		}

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

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

		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* newnode = new Node(x);
			Node* prev = cur->_prev;

			prev->_next = newnode;
			newnode->_prev = prev;

			newnode->_next = cur;
			cur->_prev = newnode;
			_size++;
			return newnode;
		}

		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;

			delete cur;
			prev->_next = next;
			next->_prev = prev;
			_size--;
			return next;
		}
		
		size_t size()
		{
			return _size;
		}

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

源码中的list实现为带头双向链表

 

list类的对象有两个成员:指向头结点的指针_head,统计数据个数的_size

在模拟实现list之前,需要先模拟实现结点类,迭代器类

结点类:三个成员,_data _prev _next实现成struct(也是类,不过与class不同的是,它的成员都是公开的,都可以在类外访问),因为这三个成员,在迭代器类中都要使用访问,设it是迭代器,*it就会返回结点中值(_data)的引用,it++(要访问结点中的_next) , it--(要访问结点中的_prev)

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

		list_node(const T& x = T())//x是匿名对象的引用,const延长了匿名对象的生命周期,x销毁,匿名对象才销毁,匿名对象会调用它的构造函数,完成初始化
			:_data(x)
			,_prev(nullptr)
			,_next(nullptr)
		{}
	};

要写构造函数:list类中的push_back插入数据时,需要new一个结点,并传数值

                         调用构造函数时,若是直接使用编译器生成的默认构造函数,则无法传参

构造函数的参数设计成缺省参数:有传实参过来就使用此值,没有传参过来就用匿名对象去拷贝构造_data,使用匿名对象去拷贝构造_data的情形:list类中的无参构造函数-->让list中的成员变量_head指向一个new出来的头结点,头结点中不存储任何有效数据,故而new时无需传参

迭代器类:一个成员 _node(结点的指针)

设计成struct:在list类中设计insert和erase时,传参给它们的是iterator迭代器,我们要使用迭代器中的成员变量(结点的指针)即要访问迭代器中的成员,那么设计成struct会比设计成class类方便不少

list的迭代器与vector,string类不同,在模拟实现vector和string的迭代器时,它们可以用原生指针来实现,因为完美契合原生指针的行为,但是模拟实现list的迭代器时,它必须设计成一个类,是对原生指针的封装,原因:

若是list的迭代器就用原生指针来实现,那么*it(it是一个迭代器)得到的不是数据,而是结点

it++ 不能走向下一个数据,故而需要对原生指针进行封装,让*it得到数据,it++,走向下一个数据

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)//要写带参的构造,因为在list类中实现begin()时,返回值的类型是迭代器,但是我们返回的可以是结点的指针,单参数的构造函数支持隐式类型转换,也可以是迭代器,但是此迭代器中的成员变量(结点的指针)需要和第一个结点的指针一样
			:_node(node)
		{}

		Ref operator*()//Ref可以是T&(普通迭代器) const T& (const迭代器)
		{
			return _node->_data;
		}

		Ptr operator->()Ptr可以是T* (普通迭代器) const T* (const迭代器)
		{
			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)
		{
			return _node != s._node;
		}

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

迭代器有非const对象调用的,也有const对象调用的,二者的区别仅仅是能否修改的问题,*迭代器返回的是数据的引用,const对象调用的迭代器返回的也是数据的引用,但是const修饰的

迭代器-> 返回的是数据的地址,const对象调用的迭代器返回的是const修饰的数据地址

(迭代器可以看作是模拟原生指针的行为,若是数据为自定义类型,那么迭代器->就可以访问到自定义类型对象中的成员,实际上看成是原生指针的迭代器-> 无法做到,但是对原生指针封装而出现的迭代器可以做到,只要迭代器->返回的是数据的指针,那么有了自定义类型对象指针,不就可以访问它里面的成员了吗)

综上来看,我们只需要写一份迭代器类的模板,根据实例化参数的不同,生成不同的迭代器

迭代器需要三个模板参数:T(迭代器类中的成员变量是结点的指针,结点实例化需要)

模板参数 Ref  : 迭代器类中重载*运算符所用,重载*运算符的函数要返回数据的引用,而若是const对象的迭代器解引用,就要用const去修饰返回值

模板参数Ptr :迭代器类中重载->运算符所用,重载->运算符的函数返回数据的指针,而若是const对象的迭代器->,也要用const去修饰返回值

那么在list类中,设计普通迭代器,const对象调用的迭代器时,就可以用迭代器类的模板,实例化参数不同就会是完全不同的类型,普通迭代器用迭代器类的模板实例化,传参 T   T&   T*

const对象调用的迭代器用迭代器类的模板实例化,传参 T  const T&   const T*

构造函数:

1 无参的构造

    让list对象中的成员变量_head指向一个头指针(双向循环)

    参照源码list的实现:

 

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

		list()
		{
			empty_init();
		}

2 拷贝构造(深拷贝)

        list(const list<T>& lt)
		{
			empty_init();
			for (auto e : lt)
			{
				push_back(e);
			}
		}

析构函数:

        ~list()
		{
			clear();
			delete _head;//释放头结点
			_head = nullptr;
		}

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

赋值重载:

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

		list<T>& operator=(list<T> lt)///lt是实参的深拷贝,lt销毁会释放掉原本由*this申请的空间
		{
			swap(lt);//交换*this和lt
			return *this;
		}

迭代器:

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

测试1:

void test1()
{
	djx::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	djx::list<int>:: iterator it = lt.begin();
	while (it != lt.end())
	{
		*it += 20;
		cout << *it << " ";
		it++;
	}
	cout << endl;

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

测试2:

void test2()
{
	djx::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	djx::list<int> lt1(lt);//拷贝构造

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

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

	djx::list<int> lt2;
	lt2.push_back(100);
	lt2.push_back(200);
	lt2.push_back(300);
	lt2.push_back(400);
	lt2.push_back(500);

	lt1 = lt2;//赋值重载

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

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

}

 

 

插入:

 1 push_back

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

2 push_front

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

3 insert

        iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;//当前节点的指针
			Node* newnode = new Node(x);
			Node* prev = cur->_prev;

			prev->_next = newnode;
			newnode->_prev = prev;

			newnode->_next = cur;
			cur->_prev = newnode;
			_size++;
			return newnode;//单参数的构造函数支持隐式类型转换
                           //或者写成:iterator(newnode)
		}

删除:

1 pop_back

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

2 pop_front

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

3 erase

        iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;

			delete cur;
			prev->_next = next;
			next->_prev = prev;
			_size--;
			return next;//返回下一个数据的地址
		}

测试3:

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

	int _a1;
	int _a2;
};

void test3()
{
	djx::list<AA> lt;
	lt.push_back(AA(1, 1));
	lt.push_back(AA(2, 2));
	lt.push_back(AA(3, 3));

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

题外:

 设计一个打印函数,打印任意list对象

template<typename T>//不可以用class
void print_list(const djx:: list<T>& lt)
{
    //list<T>未实例化的类模板,编译器不能去它里面去找
    //编译器就无法list<T>::const_iterator是内嵌类型,还是静态成员变量
    //前面加一个typename就是告诉编译器,这里是一个类型,等list<T>实例化,再去类里面找
	typename djx::list<T>::const_iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

打印任意容器

template<typename Container>
void print_container(const Container& con)
{
	typename Container::const_iterator it = con.begin();
	while (it != con.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

测试4:

void test4()
{
	djx::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	print_list(lt);
}

测试5:

void test5()
{
	djx::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	print_container(lt);

	djx::list<string> lt1;
	lt1.push_back("1111111111111");
	lt1.push_back("1111111111111");
	lt1.push_back("1111111111111");
	lt1.push_back("1111111111111");
	lt1.push_back("1111111111111");
	//print_list(lt1);
	print_container(lt1);

	vector<string> v;
	v.push_back("222222222222222222222");
	v.push_back("222222222222222222222");
	v.push_back("222222222222222222222");
	v.push_back("222222222222222222222");
	print_container(v);

}

 

 

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

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

相关文章

好用的网页制作工具就是这6个,快点来看!

对于网页设计师来说&#xff0c;好用的网页设计工具是非常重要的&#xff0c;今天本文收集了6个好用的网页设计工具供设计师自由挑选使用。在这6个好用的网页设计工具的帮助下&#xff0c;设计师将获得更高的工作效率和更精致的网页设计效果&#xff0c;接下来&#xff0c;就一…

Android studio APK切换多个摄像头(Camera2)

1.先设置camera的权限 <uses-permission android:name"android.permission.CAMERA" /> 2.布局 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"and…

vue过渡及动画

文章目录 前言类名使用自己定义动画样式多个元素过渡使用第三方库 前言 对于vue中的过渡与动画&#xff0c;官网上是这样概述的&#xff1a; Vue 在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的应用过渡效果。包括以下工具&#xff1a; 在 CSS 过渡和动画中自动…

基于空洞卷积DCNN与长短期时间记忆模型LSTM的dcnn-lstm的回归预测模型

周末的时候有时间鼓捣的一个小实践&#xff0c;主要就是做的多因子回归预测的任务&#xff0c;关于时序数据建模和回归预测建模我的专栏和系列博文里面已经有了非常详细的介绍了&#xff0c;这里就不再多加赘述了&#xff0c;这里主要是一个模型融合的实践&#xff0c;这里的数…

避雷!教你正确区分流量卡,不看可别后悔!

分不清真假流量卡&#xff1f; 想要手机流量卡&#xff0c;不小心买到了物联卡&#xff0c;结果被商家割了韭菜&#xff1f; 对于流量卡的套路太多了&#xff1f;你是否还傻傻分不清楚&#xff0c;今天&#xff0c;这篇文章教你正确区分这两种不同类型的卡。 ​ 赶紧收藏&am…

文件重命名与隐藏编号一键搞定!让不同类型的文件整齐有序

大家好&#xff01;在整理和管理不同类型的文件时&#xff0c;我们经常遇到文件名不规范、编号杂乱的情况&#xff0c;使得文件整体显得混乱无序。为了帮助您达到整齐有序的效果&#xff0c;我们自豪地推出了一款高效的工具——文件重命名与隐藏编号软件&#xff01;让您能够轻…

Redis进阶 - JVM进程缓存

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis进阶 - JVM进程缓存 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-advance-jvm-process-cache.html 传统缓存的问题 传统的缓存策略一般是请求到达 Tomcat 后&#xff0c;先查询 Redis &…

Qt开发实例_实时监测磁盘剩余空间

一、前言 当计算机磁盘空间不足,会导致应用程序无法继续存储数据,导致错误和不稳定的情况。所以,实时检测磁盘空间剩余大小对于许多应用程序来说都非常重要。 这种需求在许多应用程序中都存在,例如文件管理器、图像编辑器、视频播放器、云存储服务等等。在这些应用程序中…

Python+Appium自动化测试-编写自动化脚本

之前已经讲述怎样手动使用appium-desktop启动测试机上的app&#xff0c;但我们实际跑自动化脚本的过程中&#xff0c;是需要用脚本调用appium启动app的&#xff0c;接下来就尝试写Python脚本启动app并登陆app。环境为Windows10 Python3.7 appium1.18.0 Android手机 今日头条…

Apex导航菜单权限动态分配的实现

改动之处 return is_have_permission_wxx(V(WXX_USER_ID), 2,V(WXX_ROLE_ID)); 改之后 return is_have_permission_wxx(V(USER_ID), 2,V(ROLE_ID)); 创建新的动态菜单“动态菜单1” 共享组件→列表→创建→动态 selectlevel,m1.NAME label,f?p&||APP_ID.:||m1.…

17-会话机制

cookie 和 session cookie存储在本地 session是在服务器端进行用户信息的保存,一个字典 Cookie: # 1. 设置 : 通过response对象设置response redirect(xxx)response render_template(xxx)response Response(xxx)response make_response(xxx)response jsonify(xxx)# 通过…

xxl-job学习(一篇文章解决)

前言&#xff1a;学习xxl-job需要有git&#xff0c;springboot的基础&#xff0c;学起来就很简单 xxl-job是一个分布式的任务调度平台&#xff0c;其核心设计目标是&#xff1a;学习简单、开发迅速、轻量级、易扩展&#xff0c;现在已经开放源代码并接入多家公司的线上产品线&a…

详解过滤器Filter和拦截器Interceptor的区别和联系

目录 前言 区别 联系 前言 过滤器(Filter)和拦截器(Interceptor)都是用于在Web应用程序中处理请求和响应的组件&#xff0c;但它们在实现方式和功能上有一些区别。 区别 1. 实现方式&#xff1a; - 过滤器是基于Servlet规范的组件&#xff0c;通过实现javax.servlet.Filt…

热烈祝贺重庆融能成功入选航天系统采购供应商库

经过航天系统采购平台的严审&#xff0c;重庆融能机电设备股份有限公司成功入选中国航天系统采购供应商库。航天系统采购平台是航天系统内企业采购专用平台&#xff0c;服务航天全球范围千亿采购需求&#xff0c;目前&#xff0c;已有华为、三一重工、格力电器、科大讯飞等企业…

酒店资产如何管理提升资产利用效率

酒店资产管理系统是一种专门为酒店行业设计的管理软件&#xff0c;可以帮助酒店实现资产的全生命周期管理。一个好的酒店资产管理系统应该具备以下功能&#xff1a;  资产登记&#xff1a;可以对酒店的各种资产进行登记&#xff0c;包括房间、家具、设备等&#xff0c;记录资…

结合近日核污水排放问题浅析数字孪生技术革命对城市环境保护的作用

近期&#xff0c;日本核电站排放核污水引发全球关注&#xff0c;环境保护再次成为重要议题。随着数字孪生技术的进步&#xff0c;数字孪生技术正展现出其强大潜力&#xff0c;为环境保护提供前所未有的洞察和解决方案。本文将深入探讨数字孪生技术如何在环境保护领域发挥作用&a…

Mybatis-plus的saveBatch()造成雪花ID重复问题解析

前言 本文主要是针对Mybatis-plus框架&#xff0c;在调用 saveBatch() 方法时&#xff0c;出现的 id 重复导致的异常报错进行分析&#xff0c;提供后续场景出现相同场景时应该如何定位问题&#xff0c;如何进行调整方案。 问题分析及解决方案 一、场景分析 1、Yaml配置文件…

平衡二叉树及其应用详解

平衡二叉树 定义与性质 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;是计算机科学中的一种数据结构&#xff0c;它是二叉排序树的一种特殊情况。 平衡二叉树满足以下性质&#xff1a; 左子树和右子树的高度差不超过 1。也就是说&#xff0c;对于任意节点&#…

TopicExchange主题交换机

目录 一、简介 二、代码展示 父pom文件 pom文件 配置文件 config 生产者 消费者 测试 结果 一、简介 主题交换机&#xff0c;这个交换机其实跟直连交换机流程差不多&#xff0c;但是它的特点就是在它的路由键和绑定键之间是有规则的。 简单地介绍下规则&#xff1…

ABeam×Startup | 德硕管理咨询(深圳)创新研究团队拜访微漾创客空间

近日&#xff0c;德硕管理咨询&#xff08;深圳&#xff09;&#xff08;以下简称&#xff1a;“ABeam-SZ”&#xff09;创新研究团队前往微漾创客空间&#xff08;以下简称&#xff1a;微漾&#xff09;拜访参观&#xff0c;并展开合作交流。会议上&#xff0c;双方相互介绍了…