(C++) list底层模拟实现

 个人主页:Lei宝啊 

愿所有美好如期而遇


首先,list底层是一个带头双向循环链表,再一个,我们还要解决一个问题,list的迭代器,vector和string的迭代器可以直接++,是因为他们的地址空间是连续的,而链表不是,所以链表的迭代器封装的不是原生指针,我们需要想办法解决这个问题。

我们要解决list<T>::iterator可以++,既然我们不能封装原生指针,那么我们就对他进行运算符重载,但是在我们模拟的list类里,是不应该有这样的用法:list<int> lt, lt.operater++(),我们想要的是迭代器的运算符重载,即: list<int>::iterator it = lt.begin(); it++  这样的用法,那么我们就写一个迭代器的类,在这个类里重载++运算符。

首先是链表,我们先写出带头双向循环链表的结构,以及他的构造函数,至于为什么不用class类,是因为这样做后续可直接使用他的成员变量,如果写成类,还需要get  set函数,比较麻烦,我们模拟实现理解原理即可,所以为了省事,不使用class。

template<class T>
struct ListNode
{
	ListNode<T>* next;
	ListNode<T>* prev;
	T data;

	ListNode(const T& x = T())
		:next(nullptr)
		,prev(nullptr)
		,data(x)
	{}
};

接下来是list类的实现,第一个typedef是将链表的类型重命名,第二个typedef是将迭代器类进行重命名,第三个typedef也是将迭代器类进行重命名,并且这两个迭代器是为了区分const和非const,模版参数我们也可以看得出来。

template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	typedef __list_iterator<T, T&, T*> iterator;
	typedef __list_iterator<T, const T&, const T*> const_iterator;

    //empty_init方法是初始化一个哨兵位, 接着就是构造函数,也就是初始化一个哨兵位;
	void empty_init()
	{
		_head = new Node();
		_head->prev = _head;
		_head->next = _head;
	}

	//构造函数
	//list<int> lt(); &lt  this
	list<T>()
	{
		empty_init();
	}

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

	//拷贝构造
    //对于拷贝构造,我们需要进行深拷贝,先初始化一个哨兵位,然后遍历被拷贝的链表,             
    //用push_back方 法不断尾插节点,完成深拷贝;
	list(list<T>& copy)
	{			 
		empty_init();

		iterator it = copy.begin();
		while (it != copy.end())
		{
			push_back(it._node->data);
			it++;
		}
	}

    //最后就是赋值运算符重载,我们传参不传引用,就是为了在传参时拷贝构造出一个链表,           
    //然后我们只需要交换_head,就可以完成赋值,这里我们写一个swap方法。
	list<T>& operator=(list<T> tmp)
	{
		swap(tmp);
		return *this;
	}
    
    //push_back方法,只需要理清楚插入节点newnode和_head的关系就好;
	void push_back(const T& x = T())
	{
		//_head->prev newnode _head

		//Node* newnode = new Node(x);
		//Node* tail = _head->prev;
		//
		//tail->next = newnode;
		//newnode->prev = tail;
		//_head->prev = newnode;
		//newnode->next = _head;
		insert(end(), x);

	}

    //insert方法也是类似于push_back方法,我们写出insert方法后,                     
    //push_back完全可以复用insert方法,链表的insert没有迭代器失效问题;
	iterator insert(iterator pos, const T& x = T())
	{
		//pos->prev newnode pos
		Node* cur = pos._node;
		Node* prev = cur->prev;
	
		//prev newnode cur
		Node* newnode = new Node(x);
		newnode->next = cur;
		cur->prev = newnode;
		prev->next = newnode;
		newnode->prev = prev;

		return newnode;
	}

    //begin和end方法,也就是返回链表的开始位置和尾部位置,注意,                             
    //开始位置是_head->next,尾部位置是_head->prev;
	iterator begin()
	{
		return iterator(_head->next);
	}

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

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

	const_iterator end() const
	{
		return _head;
	}

    //erase方法我们使用时还是要注意迭代器失效问题,删除一个节点后,                          
    //迭代器就是一个野指针,也就失效了;
	iterator erase(iterator pos)
	{
		Node* cur = pos._node;
		Node* prev = cur->prev;
		Node* next = cur->next;

		prev->next = next;
		next->prev = prev;

		return iterator(next);
	}

    //clear方法,我们需要遍历链表释放所有节点,我们这里可以复用erase方法;
	void clear()
	{
		iterator it= begin();
		while (it != end())
		{
			erase(it++);
		}
	}

    //析构函数,我们调用clear释放掉其他节点后,我们再单独释放掉哨兵位;
	~list()
	{
		clear();

		delete _head;
		_head = nullptr;
	}

private:
	Node* _head;

};

__list_iterator类的实现

模版参数有三个,因为我们要明白,iterator是有解引用操作的,非const对象可以对值修改,但是const对象不可以,当然,我们可以写他的重载,但是有了这样一个模板参数,只需要修改返回值即可,并且我们重载了->运算符,这个返回值时一个指针,我们仍然需要区分是否是const对象。

template<class T, class ref, class ptr>
struct __list_iterator
{
	typedef ListNode<T> Node;
	
public:

	//构造函数
	__list_iterator(Node* node)
		:_node(node)
	{}

	//前置++
	__list_iterator& operator++()
	{
		_node = _node->next;
		return *this;
	}

	//后置++
	__list_iterator& operator++(int)
	{
		//auto tmp = __list_iterator(this->_node);
		auto tmp = __list_iterator(*this);
		_node = _node->next;
		return tmp;
	}

	//前置--
	__list_iterator& operator--()
	{
		_node = _node->prev;
		return *this;
	}

	//后置--
	__list_iterator& operator--(int)
	{
		//auto tmp = __list_iterator(this->_node);
		auto tmp = __list_iterator(*this);
		_node = _node->prev;
		return tmp;
	}

	ref operator*()
	{
		return _node->data;
	}

	ptr operator->()
	{
		return &_node->data;
	}

	bool operator!=(const __list_iterator& x)
	{
		return _node != x._node;
	}
	
	Node* _node;
};

那么我们是如何使用的呢?

list<int>::iterator it = lt.begin(),lt是list<int>类型,非const,则调用非const begin方法,但是我们的begin返回值是_head->next,是个Node*类型的指针,我们返回值类型可是iterator,封装的是__list_iterator类,但是这个类的构造函数所传的参数是单参数,而且也是Node*,也就是说,会进行隐式类型转换,_head->next会构造出一个__list_iterator临时对象,然后进行返回,然后这个临时对象再拷贝构造it,如果编译器进行优化,也就变成了直接进行拷贝构造。

我们的主要问题,对++运算符的重载也就不是问题了,唯一需要注意的是前置和后置++,区分它们只能是通过参数了,我们一般是给后置++的参数加上int。

最后一个问题就是->的重载,如果我们使用是不是这样使用:

假设我们有一个结构体,AA{int a; int b},list<AA> lt; 我们要通过iterator访问他的成员变量,list<AA>::iterator it = lt.begin(),返回值就是AA的地址,那么我们如何访问他的成员变量呢?

it.operator->()->a,或者是it->->a?

编译器省略了一个->,所以我们使用时直接it->a即可

 

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

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

相关文章

【AJAX框架】AJAX入门与axios的使用

文章目录 前言一、AJAX是干什么的&#xff1f;二、AJAX的安装2.1 CDN引入2.2 npm安装 三、基础使用3.1 CDN方式3.2 node方式 总结 前言 在现代Web开发中&#xff0c;异步JavaScript和XML&#xff08;AJAX&#xff09;已经成为不可或缺的技术之一。AJAX使得网页能够在不刷新整个…

hadoop-common: CMake failed with error code 1

问题 在编译hadoop源码时遇到如下错误 hadoop-common: CMake failed with error code 1 看了这个错误表示一脸懵逼 排查 在mvn 的命令中增加 -X 和 -e mvn clean package -e -X -Pdist,native -DskipTests -Dmaven.javadoc.skip -Dopenssl.prefix/usr/local/bin/openssl 在…

3.C语言——函数

函数 1.什么是函数2.函数的分类1.库函数2.自定义函数 3.函数的参数1.实际参数&#xff08;实参&#xff09;2.形式参数&#xff08;形参&#xff09; 4.函数的声明1.同一个文件的函数声明2.多文件的函数声明 5.函数的调用6.函数的嵌套调用和链式访问1.嵌套调用2.链式访问 7.函数…

P1059 [NOIP2006 普及组] 明明的随机数————C++、Python

目录 [NOIP2006 普及组] 明明的随机数题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 解题思路Code——CCode——Python运行结果 [NOIP2006 普及组] 明明的随机数 题目描述 明明想在学校中请一些同学一起做一项问卷调查&#xff0c;为了实验的客观性&#xff0…

力扣 | 438. 找到字符串中所有字母异位词

滑动窗口解题 示例 在s里面控制一个p字符串长度的滑动窗口&#xff0c;统计该滑动窗口中的每种字符出现的次数 import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class Problem_438_FindAnagrams {public List<Integer> findAnagram…

开放签开源工具版更新至1.1版本,进一步提升电子签名服务能力

本周开放签开源工具版增加了SDK与API能力&#xff0c;更新至1.1版本&#xff0c;使开放签电子签章工具能力进一步提升。 SDK将便于java用户直接使用CA证书颁发和签名能力。API接口采用HTTP&#xff08;S&#xff09;通讯&#xff0c;JSON报文格式&#xff0c;具有跨平台、跨语…

力扣hot100 最长有效括号 动态规划

Problem: 32. 最长有效括号 文章目录 思路Code 思路 &#x1f468;‍&#x1f3eb; 参考题解 Code ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) class Solution {public int longestValidParentheses(String s){int n s.length();…

electron使用rollup打包后,运行报错Could not dynamically require……

同学们可以私信我加入学习群&#xff01; 正文开始 分析解决总结 分析 这报错信息意思是rollup不支持动态的require&#xff0c;全部报错信息为&#xff1a; Could not dynamically require “./src/cat”. Please configure the dynamicRequireTargets or/and ignoreDynamic…

spring-boot项目,mybatis只读取了父模块的mapper目录,子模块的mapper目录读取不到

spring-boot项目&#xff0c;mybatis只读取了父模块的mapper目录&#xff0c;子模块的mapper目录读取不到 问题复现问题解决 问题复现 我的mybatis配置&#xff1a; 父模块mapper目录 子模块mapper目录 运行报错&#xff1a; 找不到子模块中的mapper配置 问题解决 debug…

做完十年数据分析后的思考与总结

种一棵树最好的时间是十年前&#xff0c;其次是现在。十年了&#xff0c;本次分享大多来自工作中的日常所思所想&#xff0c;欢迎自取。 01 数据分析的本质 数据是基础&#xff0c;分析才是重点。 行业内有专门的统计岗&#xff0c;就是只负责做好数据统计就可以了&#xff0…

第一篇【传奇开心果】Vant 开发移动应用:从helloworld开始

传奇开心果系列博文 博文系列目录Vant of Vue 开发移动应用示例博文目录一、从helloworld开始二、添加几个常用组件三、添加组件事件处理四、添加页面和跳转切换路由五、归纳总结知识点六、知识点示例代码 博文系列目录 Vant of Vue 开发移动应用示例 博文目录 一、从hellow…

Mybatis面试题(四)

MyBatis 面试题 26、Mapper 编写有哪几种方式&#xff1f; 第一种&#xff1a;接口实现类继承 SqlSessionDaoSupport&#xff1a;使用此种方法需要编写mapper 接口&#xff0c;mapper 接口实现类、mapper.xml 文件。 1、在 sqlMapConfig.xml 中配置 mapper.xml 的位置 <m…

IP改编国漫市场:繁荣背后的秘密,谁将成为下一个超级IP?

近年来IP改编已经是大众主流的趋向&#xff0c;原创剧本越来越少&#xff0c;现在市面上的动画影视大都是根据现有的IP进行二次创作&#xff0c;出来的效果也都参差不齐&#xff0c;比如说根据小说改编的《斗破苍穹》、《斗罗大陆》、《师兄啊师兄》&#xff0c;或者根据漫画改…

Spring Cloud可视化智慧工地大数据云平台源码(人、机、料、法、环五大维度)

智慧工地平台是依托物联网、互联网、AI、可视化建立的大数据管理平台&#xff0c;是一种全新的管理模式&#xff0c;能够实现劳务管理、安全施工、绿色施工的智能化和互联网化。围绕施工现场管理的人、机、料、法、环五大维度&#xff0c;以及施工过程管理的进度、质量、安全三…

Python基本输入和输出

Python是一种高级编程语言&#xff0c;以其简洁易学和功能强大而闻名。在Python中&#xff0c;输入和输出是编程中至关重要的一部分&#xff0c;它们帮助程序与用户进行交互&#xff0c;以便获取输入并向用户显示输出。本文将重点介绍Python中的基本输入和输出&#xff0c;包括…

Ardupilot开源飞控之VTOL之旅:打印件清单

Ardupilot开源飞控之VTOL之旅&#xff1a;打印件清单 1. 源由2. 清单2.1 模拟VTX打印件2.2 摄像头打印件2.3 GPS & RC天线打印件2.4 飞控 & 电调打印件 3. 总结4. 参考资料 1. 源由 VTOL一直仍在角落吃灰&#xff0c;主要还是手头缺点经费&#xff0c;搞台3D打印机基本…

FPGA高端项目:Xilinx Artix7 系列FPGA纯verilog图像缩放工程解决方案 提供4套工程源码和技术支持

目录 1、前言版本更新说明给读者的一封信FPGA就业高端项目培训计划免责声明 2、相关方案推荐我这里已有的FPGA图像缩放方案本方案在Xilinx Kintex7 系列FPGA上的应用本方案在国产FPGA紫光同创系列上的应用本方案在国产FPGA高云系列上的应用 3、设计思路框架设计框图视频源选择o…

PGSQL安装PostGIS扩展模块

一、PostGIS简介 1、PostGIS介绍 PostGIS是一个空间数据库&#xff0c;空间数据库像存储和操作数据库中其他任何对象一样去存储和操作空间对象。 空间数据与数据库关联起来的三个要素&#xff1a;数据类型、索引和函数。 空间数据类型&#xff1a;用于指定图形为点&#xff0…

浏览器插件:WebScraper基本用法和抓取页面内容(不会编程也能爬取数据)

Web Scraper 是一个浏览器扩展&#xff0c;用于从页面中提取数据(网页爬虫)。对于简单或偶然的需求非常有用&#xff0c;例如正在写代码缺少一些示例数据&#xff0c;使用此插件可以很快从类似的网站提取内容作为模拟数据。从 Chrome 的插件市场安装后&#xff0c;页面 F12 打开…

实例分割模型解析:solo模型

论文链接&#xff1a;https://arxiv.org/abs/1912.04488 代码&#xff1a;https://github.com/WXinlong/SOLO 1.摘要 我们提出了一种新的、极其简单的实例分割方法。与许多其他密集预测任务&#xff08;例如语义分割&#xff09;相比&#xff0c;任意数量的实例使得实例分割更…