【C++跬步积累】—— list模拟实现(含源代码,超详细)

🌏博客主页:PH_modest的博客主页
🚩当前专栏:C++跬步积累
💌其他专栏:
🔴 每日一题
🟡 Linux跬步积累
🟢 C语言跬步积累
🌈座右铭:广积粮,缓称王!


一、总揽(三个类和相关接口)

节点类(list_node)

template<calss T>
class list_node
{
   
   
    //成员对象
    list_node<T>* _pre;
    list_node<T>* _next;
    T _val;
    
    //成员函数
    //默认构造
    list_node(const T& val=T());
};

迭代器(list_iterator)

template<class T, class Ref, class Ptr>
class list_iterator
{
   
   
public:
	//重命名
	typedef list_node<T> node;
	typedef list_iterator<T, Ref, Ptr> iterator;
	//成员变量
	node* _node;
	//构造函数
	list_iterator(node* x);
	//运算符重载
	iterator& operator++();
	iterator operator++(int);
	iterator& operator--();
	iterator operator--(int);
	bool operator==(const iterator& lt) const;
	bool operator!=(const iterator& lt) const;
	Ref operator*();
	Ptr operator->();
};

list类

//list类
template<class T>
class list
{
   
   
	typedef list_node<T> node;
public:
	typedef list_iterator<T, T&, T*> iterator;
	typedef list_iterator<T, const T&, const T*> const_iterator;
	//默认成员函数
	list();
	list(const list<T>& lt);
	~list();
	list<T>& operator=(const list<T>& lt);
	
	//迭代器相关函数
	iterator begin();
	iterator end();
	const_iterator begin() const;
	const_iterator end() const;
	
	//访问容器相关函数
	T& front();
	T& back();
	const T& front() const ;
	const T& back() const ;
	
	//插入、删除函数
	void push_back(const T& x);
	void push_front(const T& x);
	void pop_back();
	void pop_front();
	iterator erase(iterator& it);
	iterator insert(iterator pos, const T& x);
	
	//其他函数
	void clear();
	void swap(list<T>& lt);
	
private:
	node* _head;//指向链表头结点的指针
};

二、节点类的模拟实现

list底层的实现就是一个带头双向循环链表
在这里插入图片描述
因此在实现list类之前需要先实现节点类。一个节点类需要存储三个信息:数据、前一个节点的地址、后一个节点的地址。所以成员函数就知道了:数据(_val)、前驱指针(_pre)、后继指针(_next)。

对于节点类的成员函数来说,我们只需要实现一个构造函数即可,因为这个节点类的作用就是根据数据来创建一个节点即可,而节点的释放则由list的析构函数来完成。

构造函数

list_node(const T& x = T())
	:_pre(nullptr)
	, _next(nullptr)
	, _val(x)
{
   
   }

构造函数主要是存储数据,当没有传递参数时,会调用所存储类型的默认构造所构造出来的值作为参数。
例如:数据类型为int时,会调用int类型的默认构造,会将_val初始化为0;

节点类总结

template<class T>
struct list_node
{
   
   
	//成员对象
	list_node<T>* _pre;
	list_node<T>* _next;
	T _val;

	//成员函数
	list_node(const T& x = T())
		:_pre(nullptr)
		, _next(nullptr)
		, _val(x)
	{
   
   }
};

三、迭代器类的模拟实现

迭代器类的意义

与vector和string不同,他们可以直接使用原生指针,因为他们的数据是存储在一块连续的内存空间,我们可以直接通过指针进行自增、自减、解引用等操作来进行相关操作。
在这里插入图片描述

而对于list,各个节点在内存中的位置是随机的,不是连续的,所以我们不能直接通过节点指针的自增、自减。解引用等操作对相应节点的数据进行操作。
在这里插入图片描述
迭代器的意义: 让使用者可以不用关心容器的底层实现,可以用简单统一的方式对容器内部的数据进行访问,只需要将迭代器进行相应的封装即可。

迭代器的本质就是指针,既然这个原生指针不能满足我们的要求,那么我们就可以自己封装一个,对相关的操作符进行重载,让其满足相关的操作。(例如:当你使用list迭代器进行自增时,其实执行了ptr = ptr -> next;指向了下一个节点,也就是我们所希望的自增)

如果还觉得抽象,我再举个例子,原生指针就可以看成老虎,他们吃东西的时候是直接吃生肉;而迭代器封装就可以看成是人,我们吃东西的时候,需要将食物煮熟了再吃。老虎想要吃肉就可以直接吃生肉(原生指针进行相关操作时可以直接进行++、- -等操作),而我们人类吃东西时,需要将食物进行相关处理(list迭代器的相关操作符需要进行封装,以此来达到对应的操作)

总结: list迭代器类,实际上就是对节点指针进行了封装,对相关运算符进行了重载,使得节点指针的各种行为看起来和普通指针一样。

迭代器模板参数说明

我们这里使用了三个模版参数,这是为什么呢?

template<class T, class Ref, class Ptr>

在list的模拟实现中,需要有两种迭代器:普通迭代器和const迭代器。

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

不难看出,迭代器类的模版参数列表中的Ref和Ptr分别表示引用类型和指针类型。

通过下图对比一下不难看出,const和非const的类型只有三个不同,因此通过设置三个模版参数就可以将两种状态包含了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PH_modest

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值