2024年最新【C++】list模拟实现,2024年最新深度剖析原理

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

	{
		_node = _node->_prev;
		return \*this;
	}

前置减减和前置加加类似:



	Self& operator--(int)
	{
		Self tmp(\*this);
		_node = _node->_prev;
		return tmp;
	}

### 4.3 解引用


这里\*it返回的是什么值?  
 要返回的时节点里面的data



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

### 4.4 `!=`和`==`


比较两个迭代器相不相等,比较的是节点的指针



	bool operator!=(const Self& it)
	{
		return _node != it._node;
	}

重载==:



	bool operator==(const Self& it)
	{
		return _node == it._node;
	}

### 4.5 begin 和 end


begin执行第一个节点,也就是head的next



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

这里是用来匿名对象来构造迭代器:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4e8ca18dbc09489aa14cd7a4f3fe2dea.png)  
 还可以写成:  
 单参数的构造,支持隐私类型转换



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

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/02866483fcd14126bf40d461f65a7596.png#pic_center)


end指向的是head



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

### 4.6 const迭代器


不能直接在原来的迭代器上面加上const,会导致普通迭代器就不能修改了。  
 const迭代器,需要是迭代器不能修改,还是迭代器指向的内容?  
 迭代器指向的内容不能修改!const iterator不是我们需要const迭代器,所以不能在普通迭代器的前面加const。


使用就增加一个重载的const迭代器:



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

	const_iterator end() const
	{
		return _head;

那么就得单独搞一个类ListConstIterator,让const迭代器\*it不能修改:再把相同的操作符重载一下



template
struct ListConstIterator
{
typedef ListNode Node;
typedef ListConstIterator Self;

	Node\* _node;

	ListConstIterator(Node\* node)
		:\_node(node)
	{}

	// \*it
	const T& operator\*()
	{
		return _node->_data;
	}

	// it->
	const T\* operator->()
	{
		return &_node->_data;
	}

	// ++it
	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;
	}

	bool operator==(const Self& it)
	{
		return _node == it._node;
	}
};

### 4.7 迭代器优化


发现普通迭代器和const迭代器里面很多运算符都是一样的,而const迭代器里面就直是不能修改指向的内容,代码显得冗余。就可以用模板来修改这两个类,把他们两个融成一个类。  
 就是返回值的类型不同,就用模板变,用一个模板参数:`template<class T, class Ref, class Ptr>`:



template<class T, class Ref, class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	typedef ListIterator<T, Ref, Ptr> Self;

	Node\* _node;

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

	// \*it
	//T& operator\*()
	Ref operator\*()
	{
		return _node->_data;
	}

	// it->
	//T\* operator->()
	Ptr operator->()
	{
		return &_node->_data;
	}

	// ++it
	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;
	}

	bool operator==(const Self& it)
	{
		return _node == it._node;
	}
};

**本质上相当于我们写了一个类模板,编译器实例化生成两个类。**


从而在list类里面就修改为:



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

## 5. Modifiers


### 5.1 insert


insert实现在某一个位置之前插入一个节点  
 先搞一个节点,然后记录原链表pos位置的指针,然后一前一后改指向


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2fab9d93109a4e48827f9a894e8fd853.png)



	void insert(iterator pos, const T& x)
	{
		Node\* cur = pos._node;
		Node\* newnode = new Node(x);
		Node\* prev = cur->_prev;
		prev->_next = cur;
		newnode->_prev = prev;
		newnode->_next = cur;
		cur->prev = newnode;
	}

### 5.2 push\_back


新开一个节点,然后让原链表的tail指向新节点,让新节点的prev指向tail,再把head的prev改为新节点,新节点的next改为head。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/750c4638677c40d69494bc4f77e13cea.png)  
 代码实现:



   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;
	}

来测试一下:



void test_list1()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);

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


}

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ec6ced75f31f436fb26ecf539212d657.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/bdcb66a3a69e4e84a2cab7a8479d4361.png#pic_center)  
 push\_back用erase来实现会更简单:在end位置插入一个数



	void push\_back(const T& x)
	{
		insert(end(), x);
	}

### 5.3 push\_front


头插就是在begin插入一个数:



	void push\_front(const T& x)
	{
		insert(begin(), x);
	}

测试一下:


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4a75df25b3c240a3af6153b4c4cbc9ae.png)


### 5.4 erase


先记录下要删除的节点,和它前一个节点prev 还有它的后一个节点next。然后让prev的\_next 指向next;next的\_prev 指向 prev。  
 删除会导致迭代器失效的问题,为了避免,就返回删除节点的下一个位置。



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

		prev->_next = next;
		next->_prev = prev;
		delete cur;

		return iterator(next);
	}

### 5.5 pop\_back


尾删复用erase,而尾是在end前面的一个位置,所以先减减end再删除



	void pop\_back()
	{
		erase(--end());
	}

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/28d6b7b9046f45529b815ed37f94248b.png)


### 5.6 pop\_front


因为begin所在的位置就是头节点,所以直接删除begin就可以:



	void pop\_front()
	{
		erase(begin());
	}

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/53ceb4b07c744c90b13f295a62879570.png)


### 5.7 重载operator->


用户自定义一个类型:



 struct A
{
	int _a1;
	int _a2;

	A(int a1 = 0, int a2 = 0)
		:\_a1(a1)
		, \_a2(a2)
	{}
};

调用push\_back插入数据,可以是匿名对象,也可以多参数进行隐式类型转换:



	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 });

但是要把数据输出到屏幕上,使用\*it是不可以的:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/0737bbfe9f594a7c88a9326ca944ec09.png)


A是一个自定义类型,A不支持流插入,要想指出就得自己写一个重载一个。如果不想写,可以换个方式,这里的数据是公有的可以直接访问。  
 就直接\*it返回的是A,再拿到a1和a2的数据就可以:



	list<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout <<(\*it)._a1<<":" << (\*it)._a2 <<endl;
		++it;
	}
	cout << endl;

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2886717432c2435a9969ac9afbdca292.png)


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ee58b7cfa6e74025916150c4c389db16.png#pic_center)  
 这里A\*的一个指针访问数据是先解引用,返回A对象,再来.对象里面的成员变量:



	A\* ptr = &aa1;
	(\*ptr)._a1;

**迭代器就是想要模仿A\*的行为,所以迭代器就重载了一个->:it->,它返回的是data的地址。**



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

访问它里面的就是这样:



cout << it->_a1 << “:” << it->_a2 << endl;


其实是编译器为了可读性,省略了一个箭头:第一个箭头是运算符重载,就返回data里面的地址也就是A\*,第二个箭头就能访问A里面的数据了。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a34d174adee54cf28ccc6915044a85db.png)


原生指针的行为就是:



cout << it.operator->()->_a1 << “:” << it.operator->()->_a2 << endl;


测试一下都能使用:



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 });

	list<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;
		cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;

		++it;
	}
	cout << endl;
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5cec6310da1e4e39a349109065a25976.png)


### 5.8 swap


直接调用库里面的swap来交换:



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

### 5.9 clear


clear清理掉所有数据,直接复用迭代器来把数据全部删除,但是没有清理掉哨兵位head



	void clear()
	{
		iterator it = begin();
		while (it != end())
		{
			it = erase(it);
		}
	}

## 6. 附代码



#pragma once
#include
using namespace std;
#include<assert.h>

namespace bit
{
template
struct ListNode
{
ListNode* _next;
ListNode* _prev;
T _data;

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

// typedef ListIterator<T, T&, T\*> iterator;
// typedef ListIterator<T, const T&, const T\*> const\_iterator;

template<class T, class Ref, class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	typedef ListIterator<T, Ref, Ptr> Self;

	Node\* _node;

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

	// \*it
	//T& operator\*()
	Ref operator\*()
	{
		return _node->_data;
	}

	// it->
	//T\* operator->()
	Ptr operator->()
	{
		return &_node->_data;
	}

	// ++it
	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;
	}

	bool operator==(const Self& it)
	{
		return _node == it._node;
	}
};

//template<class T>
//struct ListConstIterator
//{
// typedef ListNode<T> Node;
// typedef ListConstIterator<T> Self;
// Node\* \_node;
// ListConstIterator(Node\* node)
// :\_node(node)
// {}
// // \*it
// const T& operator\*()
// {
// return \_node->\_data;
// }
// // it->
// const T\* operator->()
// {
// return &\_node->\_data;
// }
// // ++it
// 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;
// }

// bool operator==(const Self& it)
// {
// return _node == it._node;
// }
//};

template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	//typedef ListIterator<T> iterator;
	//typedef ListConstIterator<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 it(\_head->\_next);
	// return it;
	//}

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

	iterator end()
	{
		return _head;
	}

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

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

ListNode Node;
public:
//typedef ListIterator iterator;
//typedef ListConstIterator const_iterator;

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

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

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

	iterator end()
	{
		return _head;
	}

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

[外链图片转存中…(img-Um0hdFL5-1715664258687)]
[外链图片转存中…(img-XDcmqGQ6-1715664258687)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值