list下

文章目录

      • 注意:
      • 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);

	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3、非数值型的分类变量

非数值型的分类变量 有很多非数字的数据,这里介绍如何使用它来进行机器学习。 在本教程中,您将了解什么是分类变量,以及处理此类数据的三种方法。 本课程所需数据集夸克网盘下载链接:https://pan.quark.cn/s/9b4e9a1246b2 提取码:uDzP 文章目录 1、简介2、三种方法的使用1…

idea运行卡顿优化方案

文章目录 前言一、调整配置1. idea.properties2. idea.vmoptions3.heap size4.Plugins5.Inspections 总结 前言 本人电脑16G内存&#xff0c;处理器i7 10代&#xff0c;磁盘空间也够用&#xff0c;整体配置够用&#xff0c;但运行idea会很卡&#xff0c;记录优化过程&#xff…

Linux中关于head命令详解

head的作用 head用于查看文件的开头部分的内容。 head的参数 -q隐藏文件名-v 显示文件名-c<数目>显示的字节数-n<数目>显示的行数 head的案例 # 查看yum.log前五行内容 head -5 yum.log

文件操作和IO(1)

认识文件 先来认识狭义上的文件(存储在硬盘(磁盘)上).针对硬盘这种持久化的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般. 注意:硬盘 ! 磁盘 磁盘属于…

1885_Arduino 1306 OLED屏幕的使用

全部学习汇总&#xff1a; g_arduino: Arduino hack笔记 (gitee.com) 以前我基于Arduino做过一个环境信息采集的小工具&#xff0c;采集到的信息存储到SD卡中。当时的笔记&#xff1a; 341_Arduinopython分析天气变化导致颈椎病发的原因_颈椎病相关数据分析python-CSDN博客 以前…

Jan AI本地运行揭秘:首次体验,尝鲜科技前沿

&#x1f31f;&#x1f30c; 欢迎来到知识与创意的殿堂 — 远见阁小民的世界&#xff01;&#x1f680; &#x1f31f;&#x1f9ed; 在这里&#xff0c;我们一起探索技术的奥秘&#xff0c;一起在知识的海洋中遨游。 &#x1f31f;&#x1f9ed; 在这里&#xff0c;每个错误都…

关于Hive架构原理,尚硅谷

最近学习hive 时候&#xff0c;在做一个实操案例&#xff0c;具体大概是这样子的&#xff1a; 我在dataGip里建了一个表&#xff0c;然后在hadoop集群创建一个文本文件里面存储了数据库表的数据信息&#xff0c;然后把他上传到hdfs后&#xff0c;dataGrip那个表也同步了我上传到…

gin渲染篇

1. 各种数据格式的响应 json、结构体、XML、YAML类似于java的properties、ProtoBuf package mainimport ("github.com/gin-gonic/gin""github.com/gin-gonic/gin/testdata/protoexample" )// 多种响应方式 func main() {// 1.创建路由// 默认使用了2个中…

java idea 中的 Scratches and Consoles

IDEA 中&#xff0c;"Scratches and Consoles" 是一个用于临时代码编辑和交互式开发的工具窗口&#xff0c;作用如下&#xff1a;Scratches&#xff08;草稿&#xff09;&#xff1a;Scratches 是一个用于临时编写和运行代码片段的工具&#xff0c;你可以在其中创建临…

四.Winform使用Webview2加载本地HTML页面并互相通信

Winform使用Webview2加载本地HTML页面并互相通信 往期目录本节目标核心代码实现HTML代码实现的窗体Demo2代码效果图 往期目录 往期相关文章目录 专栏目录 本节目标 实现刷新按钮点击 C# winform按钮可以调用C# winform代码显示到html上点击HTML按钮可以调用C# winform代码更…

2024首更---Web Service 教程

Web Services 简介 Web Services 可使您的应用程序成为 Web 应用程序。 Web Services 通过 Web 进行发布、查找和使用。 您应当具备的基础知识 在继续学习之前&#xff0c;您需要对下面的知识有基本的了解&#xff1a; HTMLXML 如果您希望首先学习这些项目&#xff0c;请在…

万户 ezOFFICE wf_process_attrelate_aiframe.jsp SQL注入漏洞复现

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…

lv14 信号量、互斥锁、并发控制机制选择

1 信号量&#xff1a;基于阻塞的并发控制机制 a.定义信号量 struct semaphore sem; b.初始化信号量 void sema_init(struct semaphore *sem, int val); c.获得信号量P int down(struct semaphore *sem);//深度睡眠 int down_interruptible(struct semaphore *sem);//浅度…

【Linux】python版本控制

文章目录 1.查看目前python的版本2.添加软件源并更新3.选择你想要下载的版本4.警示&#xff1a;没必要设置默认版本误区千万千万不要覆盖python3软链接解决办法 5.安装pip换源 6.环境管理 网上有很多教程都是教导小白去官方下载之后编译安装。但是&#xff0c;小白连cmake是什么…

【JavaEE】CAS

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

使用xbindkeys设置鼠标侧键

1.安装如下包 sudo apt install xbindkeys xautomation 2.生成配置文件 xbindkeys --defaults > $HOME/.xbindkeysrc 3.确定侧键键号 在终端执行下面的代码&#xff1a; xev | grep button 此时会出现如下窗口&#xff0c;将鼠标指针移动到这个窗口上&#xff1a; 单…

原生微信小程AR序实现模型动画播放只播放一次,且停留在最后一秒

1.效果展示 0868d9b9f56517a9a07dfc180cddecb2 2.微信小程序AR是2023年初发布&#xff0c;还有很多问提&#xff08;比如glb模型不能直接播放最后一帧&#xff1b;AR识别不了金属、玻璃材质的模型等…有问题解决了的小伙伴记得告诉我一声&#xff09; 微信官方文档地址 3.代码…

kotlin $ (字符串模版)的使用

$ 在kotlin 中当做字符串模版使用&#xff0c;作用就是在字符串里面识别自己定义的字符 例如打印一个字符 这个时候编译就提示我们使用字符串模版的是个 $ 的作用就是识别字符串里面的i 字数有点少了&#xff0c;在写一个demo private fun String.appendArchive(): String …

AI日报:扎克伯格瞄准AGI通用人工智能

文章目录 Meta瞄准通用人工智能领域Meta的目标Meta的产品 FAIR移动和装载H100扎克伯格对人工智能竞争对手的真实动机持怀疑态度Meta抛弃了元宇宙吗&#xff1f; Meta瞄准通用人工智能领域 Meta首席执行官马克扎克伯格&#xff08;Mark Zuckerberg&#xff09;在一份可能改变全…

QT+opencv源码编译

时间记录&#xff1a;2024/1/20 一、版本介绍 QT5.12.7cmake3.22.0opencv4.5.4 二、编译步骤 &#xff08;1&#xff09;下载opencv源码&#xff0c;然后安装&#xff0c;opencv的安装即对源码的解压过程&#xff0c;解压后的文件目录如下 &#xff08;2&#xff09;openc…