[C++随笔录] list模拟实现

news2024/11/22 23:44:21

list模拟实现

  • 基本结构
    • (1)iterator类的基本结构
    • (2)Node类的基本结构
    • (3)list类的基本结构
  • 初始化
    • (1) list类的构造函数
    • (2) Node类的构造函数
    • (3) iterator类中的构造函数
  • 迭代器行为
    • (1) 前置++&& 后置++
    • (2) 前置-- && 后置--
    • (3)operator* && operator->
    • (4)operator== && operator!=
  • list类中的各种函数
    • 插入
      • (1) insert
      • (2)push_back && push_front
    • 删除
      • (1)erase
      • (2) pop_back && pop_front
    • swap && operator=
    • begin && end
    • 其他函数

基本结构

list的底层是 带头双向循环链表, 底层构架如下:

list类: 维护头节点 _head整个双链表结构
Node类: 每个节点的结构 _prev, _next, _data
iterator类: 通过类进行封装, 进而通过重载运算符来实现迭代器行为(++/ --/ */ ->等)

🗨️为什么不能跟 vector/ string类一样, 我们直接以 Node* 充当迭代器呢?

  • 首先, vector/ string类中, T*直接充当迭代器的原因就是:
    1.物理空间是连续的, 从而导致 ++/ -- 就可以指向下一个数据了
    2.T* 经过解引用就是 T, 是我们需要的数据了
    其次, list类中, Node不能直接充当迭代器 的原因:
    1.双链表的物理空间是不连续的, 从而导致 ++ 不是下一个节点
    2.Node*经过解引用就是Node, 而我们需要的数据是 _data
    通过上面的原因, 我们知道了 Node* 不能直接充当迭代器的原因了!

  • 通过封装类实现迭代器行为的原因:
    虽然Node* 并不能直接充当迭代器, 但是节点的指针有我们需要的东西:
    _next_data.
    我们就可以以Node* 为核心来封装一个类, 通过重载++/ */ -- 等运算符来实现迭代器的行为

(1)iterator类的基本结构

template<class T, class Ref, class Ptr>
struct list_iterator
{
public:
	typedef list_node<T> Node;
	typedef list_iterator<T, Ref, Ptr> self;
	
	// 还是以节点的指针来充当迭代器
	list_iterator(Node* node)
	{}

	list_iterator(const self& it)
	{}

	// 返回的还是一个迭代器
	self& operator++()
	{}

	self operator++(int)
	{}

	self& operator--()
	{}

	Ptr operator->()
	{}

	self operator--(int)
	{}

	// 还是节点指针的比较
	bool operator!=(const self& list) const
	{}

	bool operator==(const self& list) 
	{}

	// iterator 和 const_iterator
	Ref operator*()
	{}

public:
	// 成员变量
	Node* _node;
};
  1. 成员变量是 Node* _node — — 迭代器的本质还是Node*
  2. 🗨️为什么迭代器类有三个参数?
  • 首先, 我们先明确迭代器分为 普通迭代器const迭代器
    我们是想通过一个模版, 通过不同参数实例化出不同的类型来实现 普通迭代器和const迭代器
    T — — 来控制节点内部数据的类型
    Ref — — 来控制 解引用(*) 的返回类型, 即分普通迭代器返回 T&, const迭代器返回 const T&
    Ptr — — 来控制 ->的返回类型, 即普通迭代器返回T*, const迭代器返回 const T*

即, 我们在 list类中, 通过不同的参数就可以借助一个模版来实现普通迭代器 和 const迭代器: typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;

(2)Node类的基本结构

template<class T>
struct list_node
{
	list_node(const T& val = T())
	{
		_data = val;
	}

public:
	// 成员变量
	list_node<T>* _prev = nullptr;
	list_node<T>* _next = nullptr;
	T _data;
};

(3)list类的基本结构

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


	iterator begin()
	{}

	iterator end()
	{}

	const_iterator begin()const
	{}

	const_iterator end()const
	{}

	size_t size()
	{}

	list(const list<T>& tem)
	{}

	list<T>& operator=(list<T> tem)
	{}

	// 各种函数
	// ...
	// ...

private:
	// 成员变量
	Node* _head;
	size_t _size = 0;
};

初始化

(1) list类的构造函数

我们都知道, 带头双向循环链表中, 头节点的作用就是: 方便链接哨兵位的作用.
所以, 一个带头双向循环链表的初始化就是要 初始化头结点 和 连接结构.
由于有多种初始化的方式, 而头节点的初始化是最基本的⇒ 那么, 我们就把初始化头节点单独写一个函数出来👇👇👇

// 头结点的初始化
void Init()
{
	// 为头节点开空间
	_head = new Node; 
	
	// 连接结构
	_head->_next = _head;
	_head->_prev = _head;

	_size = 0;
}
  1. 默认构造函数 — — 初始化头节点
// 头结点的初始化
list()
{
	Init();
}
  1. n个val来进行初始化
list(int n, const T& val = T())
{
	// 头结点的初始化
	Init();

	while (n--)
	{
		// 借助尾插
		push_back(val);
	}

}
  1. 迭代器区间初始化
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
	Init();

	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
  1. 拷贝构造函数
list(const list<T>& tem)
{
	// 都是内置类型
	_head = tem._head;
	_size = tem._size;
}

(2) Node类的构造函数

list_node(const T& val = T())
	:_prev(nullptr)
	,_next(nullptr)
	,_data(val)
	{}

(3) iterator类中的构造函数

  1. 构造函数
    因为list类的迭代器的本质还是 Node*
    那么, iterator类的初始化只需要 节点指针, 当然成员变量也是一个节点指针
// 还是以节点的指针来充当迭代器
list_iterator(Node* node)
{
	_node = node;
}
  1. 拷贝构造函数
    拷贝构造需要用 同类型的变量来充当形参 — — 故形参的类型是 iterator<T, Ref, Ptr> , 由于前面有 typedef , 简写成 self
list_iterator(const self& it)
{
	_node = it._node;
}

迭代器行为

(1) 前置++&& 后置++

前置++: 返回++以后的迭代器
后置++: 返回++以前的迭代器
共同点: ++以后节点都要+1, 且返回的都是一个迭代器类型

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

// 后置++
self operator++(int)
{
	Node* tem = _node;
	_node = _node->_next;
	return tem;
}

(2) 前置-- && 后置–

前置–: 返回–以后的迭代器
后置–: 返回–以前的迭代器
共同点: --以后节点都要-1, 且返回的都是一个迭代器类型

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

// 后置--
self operator--(int)
{
	Node* tem = _node->_prev;
	_node = _node->_prev;
	return tem;
}

(3)operator* && operator->

// iterator 和 const_iterator
Ref operator*()
{
	return _node->_data;
}

// iterator 和 const_iterator
Ptr operator->()
{
	return &(_node->_data);
}

先看一下使用场景吧👇👇👇

struct	Person
{
public:
	// 默认构造
	Person(const string name = "", const int age = 0)
	{
		_name = name;
		_age = age;
	}

	
	// 成员变量
	string _name;
	int _age;
};

void list_test6()
{
	muyu::list<Person> lt1;
	lt1.push_back(Person("张三", 20));
	lt1.push_back(Person("李四", 21));
	lt1.push_back(Person("王五", 22));

	// 使用*
	cout << "*" << endl;
	muyu::list<Person>::iterator tem = lt1.begin();
	while (tem != lt1.end())
	{
		cout << (*tem)._name << " " << (*tem)._age << endl;
		++tem;
	}
	cout << endl;

	cout << "->" << endl;
	// 使用->
	muyu::list<Person>::iterator t = lt1.begin();
	while (t != lt1.end())
	{
		cout << t->_name << " " << t->_age << endl;
		++t;
	}
	cout << endl;

}

int main()
{
	list_test6();

	return 0;
}

运行结果:

*
张三 20
李四 21
王五 22

->
张三 20
李四 21
王五 22

(4)operator== && operator!=

迭代器相等的本质是: 对应节点的指针是否相等, 即_node是否相等

// 还是节点指针的比较
bool operator!=(const self& list) const
{
	return _node != list._node;
}

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

list类中的各种函数

插入

(1) insert

  1. 找到对应节点, 以及前节点和后节点
  2. 更新连接关系
iterator insert(iterator pos, const T& val = T())
{
	// 找节点
	Node* cur = pos._node;
	Node* tem = new Node(val);
	Node* prev = cur->_prev;

	// 更新链接关系
	prev->_next = tem;
	tem->_prev = prev;
	tem->_next = cur;
	cur->_prev = tem;

	++_size;

	return tem;

}

(2)push_back && push_front

复用insert函数


void push_back(const T& val = T())
{
	//Node* tail = _head->_prev;
	//Node* cur = new Node(val);

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

	insert(end(), val);

}


void push_front(const T& val = T())
{
	//Node* cur = new Node(val);
	//Node* next = _head->_next;

	 更新链接关系
	//_head->_next = cur;
	//cur->_prev = _head;
	//cur->_next = next;
	//next->_prev = cur;

	insert(begin(), val);

}

删除

(1)erase

  1. 找到对应节点, 以及前节点和后节点
  2. 更新连接关系
iterator erase(iterator pos)
{
	// 头节点不能删除
	assert(pos != end());
	
	// 找节点
	Node* tem = pos._node;
	Node* prev = tem->_prev;
	Node* next = tem->_next;
	
	// 更新连接关系
	prev->_next = next;
	next->_prev = prev;

	delete tem;

	--_size;

	return next;

}

(2) pop_back && pop_front

复用erase函数

void pop_back()
{
	//Node* tail = _head->_prev->_prev;

	//if (tail)
	//{
	//	tail->_next = _head;
	//	_head->_next = tail;
	//}

	erase(--end());

}

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

	//if (cur)
	//{
	//	_head->_next = cur;
	//	cur->_prev = _head;
	//}
	
	erase(begin());

}

swap && operator=

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


// 现代写法, 巧借tem的拷贝构造
list<T>& list(list<T> tem)
{
	swap(tem);
	
	return *this;
}

begin && end

单参数的构造函数支持 隐式类型装换
返回的类型是 iterator, iterator类中构造函数是单参数的, 支持隐式类型装换

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

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

const_iterator begin()const
{
	return (_head->_next);
	// return iterator(_head);
}

const_iterator end()const
{
	return (_head);
	// return iterator(_head);
}

其他函数

  1. size
size_t size()
{
	return _size;
}
  1. clear
    删除list类中的有效数据
void clear()
{
	iterator it = begin();

	while (it != end())
	{
		it = erase(it);
	}

	_size = 0;

}
  1. 析构

~list()
{
	clear();

	delete _head;
	_head = nullptr;
}

人生大病,只是一傲字. — — 王阳明
译:一个‘傲’字,是人生最大的毛病。身为子女的傲慢,必然不孝顺;身为父母的傲慢,必然不慈爱;身为朋友的傲慢,必然不守信.
阳明先生说:“人生之大病,只一傲字”. “傲”与谦虚相反,与人交往不屑与人为伍,学习上蔑视他人,似乎自已远远超乎于知识之上. 所以,阳明先生告诫人们:“谦为众善之基,傲为罪恶之魁. ”

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

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

相关文章

微机原理与接口技术

8088/8086 CPU的两种工作模式 8088/8086可工作于两种模式下&#xff1a;最小模式与最大模式 ■最小模式为单处理器模式&#xff0c;所有控制信号由微处理器产生 ■最大模式为多处理器模式&#xff0c;部分控制信号由外部总线控制器 产生 ■用于包含协处…

阿里云服务器共享型和企业级性能差异对比

阿里云ECS云服务器共享型和企业级有什么区别&#xff1f;企业级就是独享型&#xff0c;共享型和企业级云的主要区别CPU调度模式&#xff0c;共享型是非绑定CPU调度模式&#xff0c;企业级是固定CPU调度模式&#xff0c;共享型云服务器在高负载时计算性能可能出现波动不稳定&…

Pytorch之GoogLeNet图像分类

&#x1f482; 个人主页:风间琉璃&#x1f91f; 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 目录 前言 一、GoogLeNet网络结构 1.Inception 结构 (1)Inception v1 (2)…

基于FPGA的图像形态学膨胀算法实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 在FPGA中仿真结果如下所示&#xff1a; 将FPGA中的仿真结果导入到matlab显示二维图&#xff0c;效果如下&#xff1a; 2.算法运行软件版本 matla…

Java 8 CompletableFuture 学习及实践笔记

CompletableFuture 学习及实践笔记 CompletableFuture 是 Java 8 引入的一个强大的异步编程工具&#xff0c;它提供了一种简洁而灵活的方式来处理异步操作和构建复杂的异步流程。 创建 CompletableFuture 使用 CompletableFuture.supplyAsync(Supplier<U> supplier) 方…

WindTerm 安装使用教程【图解】

往期回顾 MobaXtermMobaXterm 安装使用教程【图解】-CSDN博客WindTermWindTerm 安装使用教程【图解】-CSDN博客 一、WindTerm 功能介绍 WindTerm 是一款 Github 上开源的 SSH 终端工具&#xff0c;到目前为止它已经收获了 16.9K 颗星&#xff0c;它是完全可以比肩 MobaXterm 工…

AI写稿软件,最新的AI写稿软件有哪些

写作已经成为各行各业无法绕开的重要环节。不论是企业的广告宣传、新闻媒体的报道、还是个人自媒体的内容创作&#xff0c;文字都扮演着不可或缺的角色。随着信息的爆炸式增长&#xff0c;写作的需求也不断攀升&#xff0c;这使得许多人感到困扰。时间不够用、创意枯竭、写作技…

GICI-LIB源码阅读(三)因子图优化模型

原始 Markdown文档、Visio流程图、XMind思维导图见&#xff1a;https://github.com/LiZhengXiao99/Navigation-Learning 文章目录 三、因子图优化&#xff08;FGO&#xff09;1、因子图模型2、因子图优化状态估计模型3、因子图优化求解4、Ceres 非线性最小二乘库5、GICI-LIB 中…

山西电力市场日前价格预测【2023-09-28】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-09-28&#xff09;山西电力市场全天平均日前电价为310.91元/MWh。其中&#xff0c;最高日前电价为373.27元/MWh&#xff0c;预计出现在18: 30。最低日前电价为235.17元/MWh&#xff0c;预计…

Java基础篇 IO流

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

服务器中了360勒索病毒怎么办?勒索病毒解密,数据恢复

在众多类型的勒索病毒中&#xff0c;360勒索病毒算是占比较高、恢复难度较大的一种类型了。由于很多用户是第一次遇到这种情况&#xff0c;所以中招以后往往不知道该如何处理。所以云天数据恢复中心将根据自己的经验&#xff0c;来告诉用户服务器中了360勒索病毒怎么办。 断开网…

这3个方法,堪比U盘数据恢复大师!

“我的u盘数据可能是被我误删了&#xff0c;现在把u盘插入电脑后发现里面什么文件都没有了。这种情况还有可能恢复u盘中的数据吗&#xff1f;” 在使用u盘的过程中&#xff0c;我们可能会经常遇到u盘数据丢失的情况。先不要太担心&#xff0c;今天小编就给大家介绍一些好用的u盘…

谈谈 Redis 数据类型底层的数据结构?

谈谈 Redis 数据类型底层的数据结构? RedisObject 在 Redis 中&#xff0c;redisObject 是一个非常重要的数据结构&#xff0c;它用于保存字符串、列表、集合、哈希表和有序集合等类型的值。以下是关于 redisObject 结构体的定义&#xff1a; typedef struct redisObject {…

【python入门篇】基础知识(1)

网上关于python入门到实践的文章多不胜数&#xff0c;为什么我还要写呢&#xff1f; 一个就是对于基础知识的一个温习&#xff0c;二来就是通过详细讲解知识的同时对于自己的表达能力的一个提升&#xff0c;后续文中会出现多个案例以及练习题&#xff0c;这边我会说一些重点掌握…

韩国coupang需要懂韩文吗?平台入驻条件及费用?——站斧浏览器

coupang需要懂韩文吗 Coupang是韩国Top级电商网站&#xff0c;品类繁多&#xff0c;截止2018年&#xff0c;该网站的注册会员数超过了2500万。2017年 和 2018 年&#xff0c; Coupang APP被评为韩国受欢迎的购物APP。Coupang网站的日活移动用户数量是第二名的三倍。 那么做co…

如何在linux操作系统下安装nvm

本文主要介绍如何在linux操作系统下安装nvm&#xff0c;如果想知道nvm如何在windows操作系统下使用&#xff0c;请参考文章如何通过nvm管理多个nodejs版本_nvm 查看所有node版本-CSDN博客。 1、nvm下载 nvm全称Node Version Manager&#xff0c;即Node版本管理器。访问官网地址…

CSS笔记——伪类和伪元素

1、伪类 伪类是用于为元素在某些特定状态下添加样式的选择器。它们可以让我们为用户与页面交互时元素的外观和行为进行样式定义。 常用的伪类有: :hover &#xff0c;鼠标悬停 :active :focus &#xff0c;表单聚焦 :blur ,失去表单聚焦 :link &#xff0c;未访问 :visi…

毕业生求职应聘,性格测评怎么破?

进入到了面试和性格测试环节&#xff0c;也有很多小伙伴&#xff0c;被一个叫做性格测试的家伙给“干下来了”。性格测试这个工具&#xff0c;也没有那么神秘&#xff0c;早在头几年&#xff0c;很多大公司&#xff0c;例如阿里、美的、华为&#xff0c;都能在招聘中&#xff0…

现在的国内MBA教育是否同质化太严重?

如今在国内的MBA教育领域可以说是一片欣欣向荣&#xff0c;两百余所高校开设MBA项目招生&#xff0c;而报考市场也随着时代的发展持续升温&#xff0c;但是在这背后也存在一些问题伴随发生&#xff0c;其中就是MBA项目的同质化与跟风化趋势越来越明显&#xff0c;主要有以下几个…

每天学习3个小时能不能考上浙大MBA项目?

不少考生经常会问到上岸浙大MBA项目想要复习多长时间&#xff0c;这个问题其实没有固定答案。在行业十余年的经验总结来看&#xff0c;杭州达立易考教育认为基于每一位考生的个人复习时间、个人学习能力以及原有基础情况等不同&#xff0c;复习上岸的预期分数目标也会有差异&am…