【C++】list容器

目录

一.list容器介绍

二.C++中list的基本组成

三.list容器相关接口的模拟实现 

1.push_back()

2.迭代器的begin()和end() 

 3.insert()

4.erase() 

5.pop_front()

6.pop_back()

7.size() 

8.empty()

9.析构~list()和清除数据clear()

10.拷贝构造

 11.赋值运算

四.模拟实现时所遇到的问题

1.实现链表的迭代器

(1)原生指针的正确使用

 2.自定义类型要想支持运算符重载需要自己写

3.const迭代器不能被修改

4.合并const类型迭代器和普通迭代器(模板的进阶使用) 


一.list容器介绍

  1. list是序列容器,允许在序列中的任何位置执行固定O(1)时间复杂度的插入和删除操作,并在两个方向进行迭代。
  2. list容器使用双链表实现;双链表将每个元素存储在不同的位置,每个节点通过next,prev指针链接成顺序表。
  3. list与其他标准序列容器(array,vector和deque)相比,list通常可以在容器内的任何位置插入、提取和移动元素。
  4. list与其他标准序列容器(array,vector和deque)相比,list和forward_list(单链表实现)的主要缺点是他们不能通过位置直接访问元素;例如,要访问列表中的第五个元素,必须从已知位置(开始或结束)迭代到该位置,需要哦线性时间开销。
  5. 存储密度低,list要使用一些额外的内容空间(next,prev)来保持与每个元素相关联(前后续的线性)的链接信息,从而导致存储小元素类型(如char,short,int等)的列表的存储密度低。

二.C++中list的基本组成

namespace List
{
	template <class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;

	ListNode(const T& x = T())
		:_next(nullptr)
		,_prev(nullptr)
		,_data(x)
		{}
	};

	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		//构造函数(哨兵位)
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
	private:
		Node* _head;
        size_t _size;
	};
}

三.list容器相关接口的模拟实现 

1.push_back()

void push_back(const T& x)
{
		Node* newnode = new Node(x);
		Node* tail = _head->_prev;
			
		tail->_next = newnode;
		newnode->_prev = tail;
		newnode->_next = _head;
		_head->_prev = newnode;
}

2.迭代器的begin()和end() 

typedef ListIterator<T> iterator;
iterator begin()
{
	return iterator(_head->_next);
}
iterator end()
{
	return iterator(_head);
}

 3.insert()

	    //pos位置插入
		void insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(val);
			Node* prev = cur->_prev;

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

4.erase() 

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

			prev->_next = next;
			next->_prev = prev;
			delete cur;
            _size--;
			return iterator(next);
		}

5.pop_front()

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

6.pop_back()

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

7.size() 

size_t size() const
{
	return _size;
}

8.empty()

bool empty()
{
	return _size == 0;
}

9.析构~list()和清除数据clear()

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
}
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

10.拷贝构造

        void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
		//拷贝构造
		list(list<T>& It)
		{
			empty_init();
			for (auto& e : It)//写&为了避免It是String等类进行拷贝构造
			{
				push_back(e);
			}
		}

 11.赋值运算

		void swap(list<T> It)
		{
			std::swap(_head, It.head);
			std::swap(_size, It.size);
		}
		//赋值运算  It1=It3
		list<T>& operator=(list<T> It)
		{
			swap(It);
			return *this;
		}

四.模拟实现时所遇到的问题

1.实现链表的迭代器

(1)原生指针的正确使用

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

这里和vecctor进行对比,vecor中指针T*是可以进行++操作的,而指针Node*无法进行++操作,原因为:链表的每一个节点的物理空间不是连续的。

其次,Node*进行解引用并不能得到我们想要的数据(比如1),得的的是一个Node结点。

我们称这样的指针T*为不符合我们需求的原生指针。

  • 解决方法 

1.封装一个自定义迭代器的类

2.重载运算符,掌控原生指针的行为,使其满足需求

  • 代码
template <class T>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T> Self;

		Node* _node;//内置类型,编译器自动调用拷贝构造(浅拷贝)

		ListIterator(Node*node)
			:_node(node)
		{}
		//解引用  *it
		T& 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& it)
		{
			return _node != it._node;
		}
	};
  • 注意 :该迭代器类不需要写析构函数,因为该迭代器的本质是要访问我们链表中的节点。不可以在访问完成后对该链表进行释放。

 2.自定义类型要想支持运算符重载需要自己写

  • 代码 
struct A
	{
		int _a1;
		int _a2;

		A(int a1 = 0, int a2 = 0)
			:_a1(a1)
			, _a2(a2)
		{}
	};
	void test_list2()
	{
		list<A> lt;
		A aa1(1, 1);
		A aa2 = { 1, 1 };
		lt.push_back(aa1);
		lt.push_back(aa2);
		lt.push_back(A(2, 2));
		lt.push_back({ 3, 3 });
		lt.push_back({ 4, 4 });

		A* ptr = &aa1;
		(*ptr)._a1;
		ptr->_a1;

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

  •  解决方法

1.公有数据直接访问 

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

2.使用  ->  访问

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

	while (it != lt.end())
	{	
		cout << it->_a1 << ":" << it->_a2 << endl;
		//cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;编译器转换的样子
	}

什么意思呢?

  • 图解

3.const迭代器不能被修改

  • 代码
iterator begin()
{
		return iterator(_head->_next);
}
iterator end()
{
		return iterator(_head);
}
const_iterator begin() const
{
		return iterator(_head->_next);
}
const_iterator end() const
{
		return iterator(_head);
}

 要同时写上不同版本迭代器和const版本迭代器,如果只写了const版本的迭代器会导致非const对象也可以调用const成员函数(权限缩小),造成本身不可以被修改的结果最终可以被修改。

  • 注意 

const迭代器指的是迭代器指向的内容不能被修改。

eg. iterator begin() const

const iterator 指的是迭代器本身不能被修改不是我们所需要的迭代器

4.合并const类型迭代器和普通迭代器(模板的进阶使用) 

  • 代码 
template <class T,class Ref,class Ptr>//Ref引用,Ptr指针
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T,Ref,Ptr> Self;

		Node* _node;

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

		//解引用  *it
		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& it)
		{
			return _node != it._node;
		}
	};

template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
	/*	typedef ListIterator<T> iterator;
		typedef ListIterator<T> const_iterator;*/

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

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

		const_iterator begin() const
		{
			return iterator(_head->_next);
		}
		const_iterator end() const
		{
			return iterator(_head);
		}
     }
  • 图解 

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

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

相关文章

分享几张漂亮的linux kde主题

分享几张漂亮的linux kde主题&#xff1a;在系统设置的全局主题内下载。

SpringBoot——整合Redis

目录 Redis 创建Commodity表 启动MySQL和Redis 新建一个SpringBoot项目 pom.xml application.properties Commodity实体类 ComMapper接口 ComService业务层接口 ComServiceImpl业务接口的实现类 ComController控制器 RedisConfig配置类 SpringbootRdisApplication启…

c++|多态

c|多态 1 多态的概念2 多态的定义及其实现2.1 满足多态的条件2.2 虚函数2.3 虚函数的重写2.4 析构函数适合加virtural吗2.4 C11 override 和 final2.5 三个概念的对比 3 多态的原理4 抽象类4.1 概念4.2 纯虚函数 1 多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是…

微信小程序实现容器图片流式布局功能,配合小程序原生框架使用。

小程序实现容器图片流式布局功能&#xff0c;因为目前论坛上也有很多博主出过类似的文章&#xff0c;这里我就以一个小白角度去讲一下如何实现的吧。给作者一点点鼓励&#xff0c;先点个赞赞吧&#x1f44d;&#xff0c;蟹蟹&#xff01;&#xff01; 目标 实现下方效果图 技术…

HarmonyOS鸿蒙应用开发——安装与配置

今天脑子又抽风&#xff0c;前端转完学后端之后&#xff0c;今天大周末早上醒来突然又想学鸿蒙了&#xff0c;刚好有个比赛需要用到鸿蒙&#xff0c;于是乎我就随便点开b站看了一下鸿蒙视频&#xff0c;然后马上来写这篇博客&#xff0c;后续我的鸿蒙的博客可能会跳着、不连续地…

springboot集成达梦数据库8

springboot集成达梦数据库8 官方文档&#xff1a;[https://eco.dameng.com/document/dm/zh-cn/start/java-development.html](https://eco.dameng.com/document/dm/zh-cn/start/java-development.html) 引入maven依赖 <!--添加数据库驱动安装包--> <dependency> …

十六进制转十进制

十六进制转十进制 在玩编程的时候常会碰到十六进制转换的问题。对于专业的大佬大咖这不是问题&#xff0c;小人物总会有些麻烦。我在研究调色板时也遇到进制转换问题。前些时在本站发了十进制转十六进制的博文&#xff0c;今再写十六进制转十进制的转换方法。供大家参考。 下面…

awk编辑器

目录 工作原理 命令格式 普通格式 BEGIN格式 语句循环格式 awk常见的内建变量&#xff08;可直接用&#xff09; 按行打印行内容 统计行数量 按字段输出文本 通过管道、双引号调用 Shell 命令 awk编辑器是一种流编辑器 工作原理 逐行读取文本,默认以空格或tab键为分…

光环P3O不错的一个讲座

光环P3O不错的一个讲座&#xff0c;地址&#xff1a;https://apphfuydjku5721.h5.xiaoeknow.com/v2/course/alive/l_663dc840e4b0694c62c32d1d?app_idapphfuydJkU5721&share_fromu_5c987304d8515_wH2E5HgCgx&share_type5&share_user_idu_5c987304d8515_wH2E5HgCgx…

AIGC-风格迁移-“DEADiff:稳定可控的文本到图像风格化扩散模型 “-CVPR2024

DEADiff: An Efficient Stylization Diffusion Model with Disentangled Representations 代码&#xff1a;https://tianhao-qi.github.io/DEADiff/ 论文&#xff1a;https://arxiv.org/pdf/2403.06951 本文介绍了一种名为DEADiff的方法&#xff0c;旨在解决基于扩散的文本到图…

又翻车了!谷歌急于手动删除搜索中的奇怪AI答案|TodayAI

谷歌公司近日确认&#xff0c;正在“迅速采取行动”删除一些AI工具的奇怪回应。社交媒体上充斥着谷歌新AI概览产品&#xff08;AI Overview&#xff09;说出奇怪话语的例子&#xff0c;从告诉用户在披萨上涂胶水到建议他们吃石头。这次混乱的AI发布导致谷歌不得不手动禁用某些搜…

车灯合面合壳密封使用UV胶的优缺点是什么呢?汽车车灯的灯罩如果破损破裂破洞了要怎么修复?

车灯合面合壳密封使用UV胶的优缺点是什么呢? 车灯合壳密封使用UV胶的优缺点如下&#xff1a; 优点&#xff1a; 快速固化&#xff1a;UV胶通过紫外线照射可以在短时间内迅速固化&#xff0c;大大缩短了车灯制造的工艺流程时间&#xff0c;提高了生产效率。高度透明&#xff…

二叉树——进阶(递归创建,非递归,广度优先,翻转,深度,对称)

二叉树——进阶 二叉树的递归创建非递归前中后序遍历非递归前序遍历非递归中序遍历非递归后序遍历 广度优先遍历二叉树&#xff08;层序遍历&#xff09;翻转二叉树 二叉树深度最大深度最小深度 对称二叉树 二叉树的递归创建 1&#xff0c;二叉树是一种结构相对固定的数据&…

金融信贷风控系统设计模式应用之模版方法

背景介绍 风控系统每种场景 如个人消费贷 都需要跑很多规则 规则1 申请人姓名身份证号实名验证规则2 申请人手机号码实名认证规则3 银行卡预留手机号码实名认证规则4 申请人银行卡预留手机号码在网状态检验规则5 申请人银行借记卡有效性核验规则6 户籍地址与身份证号归属地比…

微信小程序知识点1

一. 页面样式和结构 1.1 小程序组件(html) (1) 区域布局组件 view 定义块级区域&#xff0c;相当于网页中的 div 标签text 定义行内区域&#xff0c;相当于网页中的 span标签 (2) 链接跳转组件 navigator 组件相当于网页中的 a 标签&#xff0c;用来实现页面之间的跳转。 …

基于卷积神经网络的交通标志识别(pytorch,opencv,yolov5)

文章目录 数据集介绍&#xff1a;resnet18模型代码加载数据集&#xff08;Dataset与Dataloader&#xff09;模型训练训练准确率及损失函数&#xff1a;resnet18交通标志分类源码yolov5检测与识别&#xff08;交通标志&#xff09; 本文共包含两部分&#xff0c; 第一部分是用re…

力扣刷题---2206. 将数组划分成相等数对【简单】

题目描述&#x1f357; 给你一个整数数组 nums &#xff0c;它包含 2 * n 个整数。 你需要将 nums 划分成 n 个数对&#xff0c;满足&#xff1a; 每个元素 只属于一个 数对。 同一数对中的元素 相等 。 如果可以将 nums 划分成 n 个数对&#xff0c;请你返回 true &#xf…

高开高走的续作,可不止《庆余年2》

说起最近霸屏的影视剧&#xff0c;莫过于《庆余年2》。火爆全网的讨论度总归是没有辜负观众们五年的等待&#xff0c;在五月的影视市场独占鳌头已成定局。张若昀、陈道明、李沁等一众演员稳定发挥&#xff0c;剧情节奏随着故事发展渐入佳境&#xff0c;评分一路高涨。 对影视作…

【网络安全】社会工程学攻击与防范

一、社会工程学概述 1、社会工程学的定义 通过利用人们的心理弱点、本能反应、好奇心、信任、贪婪等一些心理陷阱进行的诸如欺骗、伤害、信息盗取、利益谋取等对社会及人类带来危害的行为或方法。 当网络恶意攻击者无法通过纯粹的计算机技术达到目的时&#xff0c;高超的情商…

统计信号处理基础 习题解答10-4

题目&#xff1a; 重复习题10.3&#xff0c;但条件PDF变为&#xff1a; 以及均匀先验。如果非常大&#xff0c;这样先验知识很少&#xff0c;则会出现什么情况。 解答&#xff1a; 如果记 那么&#xff0c;根据条件独立性质&#xff0c;得到&#xff1a; 其中&#xff0c;&am…