C++ ——STL容器【list】模拟实现

代码仓库:

list模拟实现

list源码

数据结构------双向链表

文章目录

  • [🍇1. 节点结构体](#🍇1. 节点结构体)
  • [🍈2. list成员](#🍈2. list成员)
  • [🍉3. 迭代器模板](#🍉3. 迭代器模板)
  • [🍊4. 迭代器](#🍊4. 迭代器)
  • [🍋5. 插入删除操作](#🍋5. 插入删除操作)
    • [🍌5.1 insert & erase](#🍌5.1 insert & erase)
    • [🍌5.2 push_back & push_front & pop_back & pop_front](#🍌5.2 push_back & push_front & pop_back & pop_front)
  • [🍍6. 构造 & 析构 & 拷贝构造](#🍍6. 构造 & 析构 & 拷贝构造)
  • [🥭7. 赋值重载](#🥭7. 赋值重载)
  • [🍓8. 获取元素个数](#🍓8. 获取元素个数)

🍇1. 节点结构体

源码的list是双向带头循环链表,所以我们定义两个节点,一个指向下一个,一个指向前一个

cpp 复制代码
template<class T>
struct list_node
{
	list_node<T>* _next;	//指向下一个节点
	list_node<T>* _prev;	//指向前一个
	T _val;	//数据
	list_node(const T& val = T())
		:_next(nullptr)
		, _prev(nullptr)
		, _val(val)
	{}
};

🍈2. list成员

list类包含一个_head头节点,然后为了方便查出当前有多少个节点,还能多定义一个_size

cpp 复制代码
template<class T>
class list
{
    typedef list_node<T> Node;
public:
    // 各类操作方法
    //...
private:
    Node* _head;
    size_t _size;
}

🍉3. 迭代器模板

源码的迭代器设置了三个模板参数:

  • T:表示指向list节点的数据类型
  • Ref:迭代器的引用类型,通常情况为T&,但也可表示const
  • Ptr:表示指向节点的指针类型,通常情况下为T*,但也可表示const迭代器,避免代码的冗余

对于string或者是vector的迭代器,对其解引用就可以表示当前的数据;而list是链表,解引用之后表示的一个节点,所以相对会麻烦一点

cpp 复制代码
//Ref T& / const T&
//Ptr T* / const T*
template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef __list_iterator<T, Ref, Ptr> self;
	typedef list_node<T> Node;
	Node* _node;
	__list_iterator(Node* node)
		:_node(node)
	{}


	Ref operator*()
	{
		return _node->_val;
	}

	Ptr operator->()
	{
		return &_node->_val;
	}
	//前置++
	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& lt)
	{
		return _node != lt._node;
	}
	bool operator==(const self& lt)
	{
		return _node == lt._node;
	}

};

Tips:

迭代器并没有写拷贝构造,那么就是默认浅拷贝。这无关影响,因为我们就是希望通过这个迭代器找到这个节点

cpp 复制代码
void test1()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
    //浅拷贝
	list<int>::iterator it = lt.begin();
    while(it!=lt.end())
    {
        cout<< *it << " ";
        ++it;
    }
    cout<<endl;
}

这里没有奔溃也是因为迭代器没有写析构函数,迭代器只是负责访问,并不负责管理

🍊4. 迭代器

cpp 复制代码
const const_iterator begin() const
{
	//单参数构造函数 隐式类型转换
	return _head->_next;
}
const const_iterator end() const
{
	return _head;
}


iterator begin()
{
	//单参数构造函数 隐式类型转换
	return _head->_next;
}
iterator end()
{
	return _head;
}

🍋5. 插入删除操作

🍌5.1 insert & erase

这里插入删除操作之后,也会存在当前迭代器失效,所以传修改完毕之后的迭代器位置

cpp 复制代码
iterator insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* tmp = new Node(x);
	Node* prev = cur->_prev;

	prev->_next = tmp;
	tmp->_next = cur;

	cur->_prev = tmp;
	tmp->_prev = prev;
	++_size;
	return tmp;
}
iterator erase(iterator pos)
{
	assert(pos != end());
	Node* cur = pos._node;
	Node* next = cur->_next;
	Node* prev = cur->_prev;

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

🍌5.2 push_back & push_front & pop_back & pop_front

写了指定位置插入删除之后,直接复用即可

cpp 复制代码
void push_back(const T& x)
{
	insert(end(), x);
}

void push_front(const T& x)
{
	insert(begin(), x);
}

void pop_back()
{
	Node* tail = _head->_prev;
	erase(tail);
}
void pop_front()
{
	erase(begin());
}

🍍6. 构造 & 析构 & 拷贝构造

查看源码发现list的构造和析构都采用了复用

清空链表

cpp 复制代码
void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
	//_size = 0;
}

复用

cpp 复制代码
void empty_init()
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;
}

list()
{
	empty_init();
}

list(const list<T>& lt)
{
	empty_init();
	for (auto& e : lt)
	{
		push_back(e);
	}
}
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

🥭7. 赋值重载

这里还是采用现代的写法,交换完毕之后,自动调用析构函数

cpp 复制代码
void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
	std::swap(_size, lt._size);
}
list<T>& operator=(list<T> lt)
{
	swap(lt);
	return *this;
}

🍓8. 获取元素个数

cpp 复制代码
size_t size()
{
	return _size;
}

以上就是list的基本功能实现,实质上就是双向带头循环链表,迭代器这块有点复杂。

那本期分享就到这里咯,我们下期再见,如果还有下期的话。

相关推荐
阿巴~阿巴~1 小时前
深入解析C++ STL链表(List)模拟实现
开发语言·c++·链表·stl·list
旺小仔.2 小时前
双指针和codetop复习
数据结构·c++·算法
jingfeng5142 小时前
C++ STL-string类底层实现
前端·c++·算法
郝学胜-神的一滴2 小时前
基于C++的词法分析器:使用正则表达式的实现
开发语言·c++·程序人生·正则表达式·stl
网硕互联的小客服3 小时前
Apache 如何支持SHTML(SSI)的配置方法
运维·服务器·网络·windows·php
etcix3 小时前
implement copy file content to clipboard on Windows
windows·stm32·单片机
许泽宇的技术分享4 小时前
Windows MCP.Net:基于.NET的Windows桌面自动化MCP服务器深度解析
windows·自动化·.net
努力努力再努力wz4 小时前
【c++深入系列】:万字详解模版(下)
java·c++·redis
非凡ghost5 小时前
AMS PhotoMaster:全方位提升你的照片编辑体验
windows·学习·信息可视化·软件需求
瓦特what?5 小时前
关于C++的#include的超超超详细讲解
java·开发语言·数据结构·c++·算法·信息可视化·数据挖掘