list下

news2024/11/26 22:35:56

文章目录

      • 注意:
      • const迭代器
        • 怎么写?
        • 运用场合?
      • insert
      • erase
      • 析构函数
      • 赋值和拷贝构造
        • 区别?
        • 拷贝构造不能写那个swap,为什么?
        • 拷贝构造代码
      • 面试问题
        • 什么是迭代器失效?
        • vector、list的区别?
      • 完整代码

注意:

delete 和delete[]之间不能用错
delete是单个;
delete是一个数组;
vector、string都是delete[];
list本质不是数组是列表,所以delete

const迭代器

怎么写?
		typedef __list_iterator<T, T&, T*> iterator; // 放public里面不然外面动不了T,就是用不了模板,私有的不能用
		typedef __list_iterator<T,const T&,const T*> const_iterator;//const迭代器

		//迭代器用节点的指针就行了
		iterator begin()
		{
			return iterator(_head->_next);
			//注意这里返回的是迭代器,不是_head头部指针、这个是list类里面的成员变量
			//现在是把头部指针_head传过去,然后迭代器调用他的拷贝函数;
			//这里面就是构造函数、因为传过去的是Node类型;
		}
		iterator end()
		{
			return iterator(_head);
		}
		//const_iterator
		const_iterator begin() const
		{
			return const_iterator(_head->_next); 
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}

image.png
模板里面放多个参数,在传模板时候,如上图,右边框起来上面是普通的迭代器,下面有const,用哪种就会自动传过去,同时Self也是对应的迭代器;
image.png

运用场合?

一般const指针是不用的;
什么情况下去用?
传参给另一个函数用的时候会用到;
例如下面功能是打印的成员函数

	void print_list(const list<int>& lt)
	{
		list<int>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

insert

在pos位置插入,这个pos是一个迭代器,插入后这个节点以及前后节点的_next和_prev要注意怎么赋值;
cur是pos指向的当前节点;
newnode是新插入的节点;
image.png

		//insert
		//现在要的是列表里面的那两个指针next\prev;
		//迭代器里面就有_node存的是Node*类型;
		void insert(iterator pos,const T& x = T())
		{
			Node* cur = pos._node;//pos是迭代器所以调用他的成员函数用点符号"."虽然类似指针但是不能->,这个是指针用的
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

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

		}

写完insert,push_back和push_front就可以直接用insert的

		//头插
		void push_front(const T& x = T())
		{
			insert(begin(), x);
		}
		//尾插
		void push_back(const T& x = T())
		{
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);

			//tail->_next = newnode;
			//newnode->_prev = tail;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			

			//再pos是end()的时候插入,end()表示最后一个元素的后一个位置,所以插入这个位置就是尾插
			insert(end(), x);
		}

erase

头节点指向的迭代器就是end();
end()的意思是最后一个元素的后一个位置,那就是_head,这个头节点;
和insert类似,_next/_prev都要重新赋值一下:
image.png

		//erase,erase是要返回删除的位置的后一个位置的
		Node* erase(iterator pos)
		{

			//cur里面存现在的地址
			//prev是cur上一个地址
			assert(pos  != end()); //头节点不能删
			Node* cur = pos._node;  
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;

			delete cur;//delete[]必须是数组,所以要注意
			return next;
			

		}

头删除、和尾删除就可以用erase了

		//尾删
		void pop_back()
		{
			//erase(iterator(_head->_prev));
			erase(--end());
		}
		//头删除
		void pop_front()
		{
			erase(begin());
		}

析构函数

析构,栈里面的是先定义的后出去,现进后出;
析构是释放了还要置空;

		void clear()
		{
			Node* cur = _head->_next;

			while (cur != _head)
			{
				_head->_next = cur->_next;
				delete cur;
				cur = _head->_next
			}
			_head = head->next = head->prev;//重新关联起来,双向带头循环列表;
		}
		//析构
		~list()
		{
			clear();
			detete _head;
			_head = nullptr;
		}

clear的作用是清空这个链表里面的内容,_head这个头节点是没有data的,所以是不用动的;
析构是直接这个链表对象空间释放了,所以_head也要释放;

赋值和拷贝构造

区别?

问:string为什么这边写了要先置位nullptr,然而这边的list没有呢?
答:
拷贝构造l1(l2),l1首先要初始化、至少里面是个nullptr,因为他还没有被创建出来;
赋值的话l1 = l2,首先你这个l1是不是已经是先定义出来的,所以不用初始化的;
image.png

拷贝构造不能写那个swap,为什么?

答:这样的话在传参数时就是又一个拷贝构造,那就会无限循环

拷贝构造代码
		//拷贝函数l1(l2)
		//首先l1是没有创建出来的,所以要初始化
		list(const list<T>& lt)
		{
			//先初始化,list里面成员函数是什么?_head,_head里面的内容
			Node* _head;
			_head->_next = _head->_prev = _head;

			//往里面插入后面的值就行了
			//方法一
			/*const_iterator it = lt.begin();
			while (it != it.end())
			{
				push_back(*it);//运算符重载过*it了,传过去的是_node->_data
				it++;
			}*/

			//方法二
			for (auto e : lt)//本质调用的迭代器
			{
				push_back(e);
			}

		}

在string和vector里面是这样的,for循环本质使用的是迭代器,有迭代器就有for循环,e理论上就是*it
image.png

面试问题

什么是迭代器失效?

迭代器失效具体内容

vector、list的区别?

vector是一个动态增长的数据
优点:支持随机访问operator[],排序、二分查找、堆算法等等可用到;
缺点:空间不够要增容、头部或者中间插入效率低o(n);

list是一个带头双向循环的链表
优点:任意位置插入效率高o(1);
缺点:不支持随机访问;

总结:list和vector相辅相成、是两个互补的容器

完整代码

test.c
在这里插入图片描述

#pragma once
namespace list_test
{
	template<class T>
	//struct__list_node,两个_这种取名方式一般表示一会这个会在别的里面用,在这里就表示,list类里面直接用的类外面定义的struct __list_code这个结构体
	struct __list_node
	{
		__list_node<T>* _next; // 别忘了这里要写<T>
		__list_node<T>* _prev;
		T _data;

		//列表需要构造函数来初始化
		__list_node(const T& x = T())//这个T(),指的是没有传值过来那就直接为0
			:_data(x)
			,_next(nullptr)  
			,_prev(nullptr)
		{}
	};


	//struct迭代器__list_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;
		 
		//构造函数,struct怎么写构造函数、就是看这个里面成员函数有哪些
		__list_iterator(Node* node)
			:_node(node)
		{}

		//构造传的是里面的成员函数、拷贝是传的类或者说这个结构体
		//浅拷贝,这个是默认函数、不用写的
		__list_iterator(const Self& it) 
		{
			_node = it._node;
		}

		// operator*,读_node中的data,data的类型是T,所以函数返回值类型是T
		Ref operator*()
		{
			return _node->_data;
		}


		//++
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		Self operator++(int) //后置++里面就放个int,这是设计出来的伪参数,用来区分前后置用的
		{
			__list_iterator<T> tmp(*this); //直接拷贝函数、这个不用写、因为是浅拷贝,深拷贝才要写
			_node = _node->_next; 
			//++(*this);
			return tmp;
		}

		//--
		Self& operator--()
		{
			_node = _node->_prev; //_node是指针
			return *this; //*this就是迭代器,this是指针指向迭代器
		}
		Self& operator--(int) //后置++里面就放个int,这是设计出来的伪参数,用来区分前后置用的
		{
			Self tmp(*this); //直接拷贝函数、这个不用写、因为是浅拷贝,深拷贝才要写
			//node = _node->_next;
			--(*this);
			return tmp;
		}




		//!=,两个迭代器之间不相等、迭代器里面的成员函数是_node,比的是这两个,就是他指向一个列表的地址,看这两个地址一样吗
		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		Ptr operator->()
		{
			return &_node->_data;//返回这个地址
			//指向这个地址,调用时第一个->返回其地址、第二个->返回地址上的值,迭代器做了优化,调用只需要一个->
		}


	};




	template<class T>
	class list
	{
		typedef __list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator; // 放public里面不然外面动不了T,就是用不了模板,私有的不能用
		typedef __list_iterator<T,const T&,const T*> const_iterator;//const迭代器

		//迭代器用节点的指针就行了
		iterator begin()
		{
			return iterator(_head->_next);
			//注意这里返回的是迭代器,不是_head头部指针、这个是list类里面的成员变量
			//现在是把头部指针_head传过去,然后迭代器调用他的拷贝函数;
			//这里面就是构造函数、因为传过去的是Node类型;
		}
		iterator end()
		{
			return iterator(_head);
		}
		//const_iterator
		const_iterator begin() const
		{
			return const_iterator(_head->_next); 
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}



		//构造函数
		list()
		{
			_head = new Node;//new node //这里面不用写成Node[],是因为[]是数组用的,这个就里面就开辟一个指针指向的地址空间,[]是一个数组的空间
			_head->_next = _head;
			_head->_prev = _head;



		}


		//insert
		//现在要的是列表里面的那两个指针next\prev;
		//迭代器里面就有_node存的是Node*类型;
		void insert(iterator pos,const T& x = T())
		{
			Node* cur = pos._node;//pos是迭代器所以调用他的成员函数用点符号"."虽然类似指针但是不能->,这个是指针用的
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

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

		}
		//头插
		void push_front(const T& x = T())
		{
			insert(begin(), x);
		}
		//尾插
		void push_back(const T& x = T())
		{
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);

			//tail->_next = newnode;
			//newnode->_prev = tail;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			

			//再pos是end()的时候插入,end()表示最后一个元素的后一个位置,所以插入这个位置就是尾插
			insert(end(), x);
		}


		//erase,erase是要返回删除的位置的后一个位置的
		Node* erase(iterator pos)
		{

			//cur里面存现在的地址
			//prev是cur上一个地址
			assert(pos  != end()); //头节点不能删
			Node* cur = pos._node;  
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;

			delete cur;//delete[]必须是数组,所以要注意
			return next;
			

		}

		//尾删
		void pop_back()
		{
			//erase(iterator(_head->_prev));
			erase(--end());
		}
		//头删除
		void pop_front()
		{
			erase(begin());
		}

		void clear()
		{
			Node* cur = _head->_next;

			while (cur != _head)
			{
				_head->_next = cur->_next;
				delete cur;
				cur = _head->_next
			}
			_head = head->next = head->prev;//重新关联起来,双向带头循环列表;
		}
		//析构
		~list()
		{
			clear();
			detete _head;
			_head = nullptr;
		}

		//拷贝函数l1(l2)
		//首先l1是没有创建出来的,所以要初始化
		list(const list<T>& lt)
		{
			//先初始化,list里面成员函数是什么?_head,_head里面的内容
			Node* _head;
			_head->_next = _head->_prev = _head;

			//往里面插入后面的值就行了
			//方法一
			/*const_iterator it = lt.begin();
			while (it != it.end())
			{
				push_back(*it);//运算符重载过*it了,传过去的是_node->_data
				it++;
			}*/

			//方法二
			for (auto e : lt)//本质调用的迭代器
			{
				push_back(e);
			}

		}


		//赋值运算l1 = l2
		//方法一:
		list<T>& operator=(const list<T>& lt)
		{
			//防止自己给自己赋值
			if (this != &lt)//用地址来比较
			{
				clear();//看clear函数,clear结束后就是初始化后的_head,所以也不用再写一遍_head的初始话
				for (auto e : lt)
					push_back(e);//就一行不用写{}了
				
			}

			return *this;//*this,解引用this迭代器就是这个list类的对象了l1
		}
		//方法二:
		list<T>& operator=(list<T> lt)//这里传参用到拷贝构造,lt是临时对象
		{
			swap(_head, lt._head)
			return *this;//*this,解引用this迭代器就是这个list类的对象了l1
		}


	private:
		Node* _head;
	};


	void test_list1()
	{
		list<int> lt;

		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

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

	}
	

	struct Date
	{
		int _year = 0;
		int _month = 1;
		int _day = 2;

	};

	void test_list2()
	{
		list<Date> lt;
		lt.push_back(Date());
		lt.push_back(Date());

		list<Date>::iterator it = lt.begin();//调用data,因为现在模板T就是date,传参传过去date是可以的
		while (it != lt.end())
		{
			cout << it->_year << " " << it->_month << " " << it->_day;
			++it;
		}
		cout << endl;
	}

	void print_list(const list<int>& lt)
	{
		list<int>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}


	void test_list3()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		print_list(lt);


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

	}
}

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

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

相关文章

微信公众号服务器配置启用, Java示例

微信公众号接入指南文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html 在微信公众号的服务器配置中&#xff0c;首先需要编写Java代码来处理微信服务器的验证请求。以下是Java示例代码&#xff0c;然后我将为你提供启用服务器配置…

centos7安装Redis7.2.4

文章目录 下载Redis解压Redis安装gcc依赖&#xff08;Redis是C语言编写的&#xff0c;编译需要&#xff09;编译安装src目录下二进制文件安装到/usr/local/bin修改redis.conf文件启动redis服务外部连接测试 参考&#xff1a; 在centos中安装redis-5.0.7 Memory overcommit must…

记录一下uniapp 集成腾讯im特别卡(未解决)

uniapp的项目运行在微信小程序 , 安卓 , ios手机三端 , 之前这个项目集成过im,不过版本太老了,0.x的版本, 现在需要添加客服功能,所以就升级了 由于是二开 , 也为了方便 , 沿用之前的webview嵌套腾讯IM的方案 , 选用uniapp集成ui ,升级之后所有安卓用户反馈点击进去特别卡,几…

华为FusionStorage Block、OceanStor 100D、OceanStor pacific的区别

华为FusionStorage Block、OceanStor 100D、OceanStor pacific的区别&#xff1f; 华为块存储到底是叫什么呢&#xff1f; 有接触过华为块存储产品的小伙伴肯定都有疑惑&#xff0c;在FusionStorage 、FusionStorage Block、OceanStor 100D、OceanStor pacific等等的名词中&a…

基于SpringBoot Vue高校失物招领系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

自然语言推断:注意力之注意(Attending)

注意&#xff08;Attending&#xff09; 第一步是将一个文本序列中的词元与另一个序列中的每个词元对齐。假设前提是“我确实需要睡眠”&#xff0c;假设是“我累了”。由于语义上的相似性&#xff0c;我们不妨将假设中的“我”与前提中的“我”对齐&#xff0c;将假设中的“累…

【复现】Apache Solr信息泄漏漏洞_24

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 Apache Solr是一个独立的企业级搜索应用服务器&#xff0c;它对外提供类似于Web-service的API接口。用户可以通过http请求&#x…

《Python数据分析技术栈》第03章 03 可视化各级数据(Visualizing various levels of data)

03 可视化各级数据&#xff08;Visualizing various levels of data&#xff09; 《Python数据分析技术栈》第03章 03 可视化各级数据&#xff08;Visualizing various levels of data&#xff09; Whenever you need to analyze data, first understand if the data is stru…

Node.JS CreateWriteStream(大容量写入文件流优化)

Why I Need Node.JS Stream 如果你的程序收到以下错误&#xff0c;或者需要大容量写入很多内容(几十几百MB甚至GB级别)&#xff0c;则必须使用Stream文件流甚至更高级的技术。 Error: EMFILE, too many open files 业务场景&#xff0c;我们有一个IntradayMissingRecord的补…

网络数据传输过程

先验知识&#xff1a;OSI模型 OSI网络模型实际上是参考模型&#xff0c;在实际中并不使用&#xff0c;在网络出现问题的时候&#xff0c;可以从一个宏观的整体去分析和解决问题&#xff0c;而且搭建网络的时候并不需要划分为7层&#xff0c;当今互联网广泛使用的是TCP/IP网络模…

Leetcode 2788. 按分隔符拆分字符串

我们可以先自己模拟一下分隔字符串的过程。如果只是简单的&#xff0c;遇到分隔符&#xff0c;将分隔符前后的子串加入结果的List&#xff0c;那么很显然并没有考虑到一个String中有多个字符串的情况。一种比较容易想到的方法是&#xff1a; 先对List中每个字符串遍历&#xf…

Docker-Confluence部署记录

启动 docker container run -v $(pwd):/var/atlassian/application-data/confluence/ --nethost -d --nameconfluence_720_20240120 confluence/confluence:7.2.0新建mysql数据库 导入破解包 atlassian-agent 参考-Confluence 破解方式&#xff08;Linux&#xff09; 按流程破…

设计模式-资源库模式

设计模式专栏 模式介绍模式特点应用场景资源库模式与关系型数据库的区别代码示例Java实现资源库模式Python实现资源库模式 资源库模式在spring中的应用 模式介绍 资源库模式是一种架构模式&#xff0c;介于领域层与数据映射层&#xff08;数据访问层&#xff09;之间。它的存在…

Spring Boot 3.2.2整合MyBatis-Plus 3.5.5依赖不兼容问题

问题演示 导依赖 当你启动项目就会 抛出该异常 java.lang.IllegalArgumentException: Invalid value type for attribute factoryBeanObjectType: java.lang.String 问题原因 mybatis-plus 中 mybatis 的整合包版本不够导致的 解决方案 排除掉mybatis-plus 中 mybatis 的整合…

k8s 使用cert-manager证书管理自签

个人建议使用安装更快&#xff0c;比helm快&#xff0c;还要等待安装crd kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml#官网 https://cert-manager.io/docs/installation/kubectl/#创建自签的ClusterIssuer c…

基于YOLOv8的学生课堂行为检测,引入BRA注意力和Shape IoU改进提升检测能力

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文摘要&#xff1a;介绍了学生课堂行为检测&#xff0c;并使用YOLOv8进行训练模型&#xff0c;以及引入BRA注意力和最新的Shape IoU提升检测能力 1.SCB介绍 摘要&#xff1a;利用深度学习方法自动检测学生的课堂行为是分析学生课堂表…

基于SpringBoot Vue养老院管理

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

【iOS】CollectionView、瀑布流式和圆环布局

使用UITableView作为表格来展示数据完全没有问题&#xff0c;但仍有许多局限性&#xff0c;对于一些更加复杂的布局样式&#xff0c;就有些力不从心了 比如&#xff0c;UITableView只允许表格每一行只能显示一个cell&#xff0c;而不能在一行中显示多个cell&#xff0c;对于这…

智慧工地解决方案及案例:PPT全文26页,附下载

关键词&#xff1a;智慧工地解决方案&#xff0c;智慧工地建设&#xff0c;智慧工地整体架构&#xff0c;数字化工地&#xff0c;智慧工程 一、智慧工地建设对传统建筑业的影响 1、提高了施工效率和质量&#xff1a;智慧工地建设采用了先进的信息技术和管理方法&#xff0c;可…

【C++】vector容器接口要点的补充

接口缩容 在VS编译器的模式下&#xff0c;类似于erase和insert接口的函数通常会进行缩容&#xff0c;因此&#xff0c;insert和erase行参中的迭代器可能会失效。下图中以erase为例&#xff1a; 代码如下&#xff1a; #include <iostream> #include <vector> #inclu…