C++ list模拟实现

目录

一、节点

[二、 迭代器](#二、 迭代器)

三、双向链表

四、测试代码


一、节点

cpp 复制代码
template<class T>
struct list_node
{
	list_node<T>* _next;
	list_node<T>* _prev;
	T _data;

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

这段代码定义了一个模板结构list_node,它是一个双向链表的节点结构。

在这个结构中,有三个成员:

  1. _next:这是一个指向下一个节点的指针。它的类型是list_node<T>*,表示它指向的是一个list_node类型的对象。

  2. _prev:这是一个指向前一个节点的指针。它的类型也是list_node<T>*,表示它指向的是一个list_node类型的对象。

  3. _data:这是节点存储的数据。它的类型是T,这是一个模板参数,表示数据可以是任何类型。

此外,这个结构还有一个构造函数list_node(const T& x = T()),它接受一个类型为T的参数x,并将x赋值给_data。这个构造函数的参数有一个默认值T(),表示如果在创建list_node对象时没有提供参数,那么_data将被初始化为T类型的默认值。同时,_next_prev被初始化为nullptr,表示新创建的节点没有连接到任何其他节点。

二、 迭代器

cpp 复制代码
template<class T,class Ref,class Ptr>
struct _list_iterator
{
	typedef list_node<T> node;
	typedef _list_iterator<T, Ref,Ptr> self;
	node* _node;

	_list_iterator(node* n)
		:_node(n)
	{}

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

	Ptr operator->()
	{
		return &_node->_data;
	}

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

	self operator++(int)
	{
		self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}

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

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

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

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

这段代码定义了一个模板结构_list_iterator,它是一个双向链表的迭代器。

在这个结构中,有以下几个部分:

  1. typedef list_node<T> node;typedef _list_iterator<T, Ref,Ptr> self;:这两行代码定义了两个类型别名,node代表链表节点的类型,self代表迭代器自身的类型。

    typedef ListIterator<T, Ref, Ptr>的作用是定义了一个新的类型名ListIterator,这个类型名后面可以直接接模板参数,用于创建特定类型的迭代器。

    例如,ListIterator<int, int&, int*>就是一个可以用于遍历int类型链表的迭代器,它的引用类型和指针类型也都是int的引用和指针。

    这样做的好处是,我们可以根据需要创建不同类型的迭代器,而不需要为每一种类型都写一个新的迭代器类。这大大提高了代码的复用性和可维护性。

  2. node* _node;:这是迭代器内部的一个私有成员,它是一个指向链表节点的指针,用于在链表中移动。

  3. _list_iterator(node* n) : _node(n) {}:这是迭代器的构造函数,它接受一个节点指针作为参数,并将这个指针赋值给_node

接下来是一些重载的操作符,它们使得我们可以像使用指针一样使用迭代器:

  1. Ref operator*():这是解引用操作符,它返回当前迭代器指向的节点的数据。

  2. Ptr operator->():这是箭头操作符,它返回当前迭代器指向的节点的数据的地址。

  3. self& operator++()self operator++(int):这是前置和后置的++操作符,它们使得迭代器向后移动一位。

  4. self& operator--()self operator--(int):这是前置和后置的--操作符,它们使得迭代器向前移动一位。

  5. bool operator==(const self& s)bool operator!=(const self& s):这是等于和不等于操作符,它们用于比较两个迭代器是否相等。

三、双向链表

cpp 复制代码
template<class T>
class list
{
	typedef list_node<T> node;
public:
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, constT*>const_iterator;
	
	iterator begin()
	{
		return iterator(_head->_next);
	}
	
	const_iterator begin() const
	{
		return const_iterator(_head->next);
	}

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

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

	void empty_init()
	{
		_head = new node;
		_head->_prev = _head;
		_head->_next = _head;
	}

	list()
	{
		empty_init();
	}

	template <class Iterator>
	list(Iterator first, Iterator last)
	{
		empty_init();

		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

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

	list(const list<T>& lt)
	{
		empty_init();

		list<T> tmp(lt.begin(), lt.end());
		swap(tmp);
	}

	list<T>& operator=(list<T> lt)
	{
		swap(lt);
		return *this;
	}

	~list()
	{
		clear();
		delete _head;
		_head = nullptr;
	}

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

	void push_back(const T& x)
	{
		insert(end(), x);
	}

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

	void pop_back()
	{
		erase(--end());
	}

	void pop_front()
	{
		erase(begin());
	}

	void insert(iterator pos, const T& x)
	{
		node* cur = pos._node;
		node* prev = cur->_prev;

		node* new_node = new node(x);

		prev->_next = new_node;
		new_node->_prev = prev;
		new_node->_next = cur;
		cur->_prev = new_node;
	}

	iterator erase(iterator pos)
	{
		assert(pos != end());

		node* prev = pos._node->_prev;
		node* next = pos._node->_next;

		prev->_next = next;
		next->_prev = prev;
		delete pos._node;

		return iterator(next);
	}

private:
	node* _head;
};

这段代码定义了一个模板类list,它是一个双向链表的实现。

在这个类中,有以下几个部分:

cpp 复制代码
template<class T>
class list
{
    typedef list_node<T> node;
public:
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, constT*>const_iterator;

这些行定义了一些类型别名,node代表链表节点的类型,iterator代表链表迭代器的类型,const_iterator代表常量链表迭代器的类型。
2. node* _head;:这是链表的头节点,它是一个私有成员。

接下来是一些成员函数:

  1. iterator begin()const_iterator begin() const:这两个函数返回链表的开始迭代器,也就是指向链表第一个元素的迭代器。

  2. iterator end()const_iterator end() const:这两个函数返回链表的结束迭代器,也就是指向链表尾部之后位置的迭代器。

  3. void empty_init():这个函数用于初始化一个空的链表,链表的头节点指向自己。

  4. list():这是链表的默认构造函数,它调用empty_init()来初始化一个空的链表。

  5. list(Iterator first, Iterator last):这是链表的另一个构造函数,它接受两个迭代器参数,然后将这两个迭代器之间的元素添加到链表中。

  6. void swap(list<T>& tmp):这个函数用于交换两个链表。

  7. list(const list<T>& lt):这是链表的拷贝构造函数,它创建一个新的链表,然后将传入的链表的元素复制到新的链表中。

  8. list<T>& operator=(list<T> lt):这是链表的赋值运算符,它使用了拷贝-交换技术,先创建一个临时链表,然后交换临时链表和当前链表。

  9. ~list():这是链表的析构函数,它首先调用clear()函数删除所有的元素,然后删除头节点。

  10. void clear():这个函数用于删除链表中的所有元素。

  11. void push_back(const T& x)void push_front(const T& x):这两个函数用于在链表的尾部和头部添加元素。

  12. void pop_back()void pop_front():这两个函数用于删除链表的尾部和头部的元素。

  13. void insert(iterator pos, const T& x):这个函数用于在指定位置插入一个元素。

  14. iterator erase(iterator pos):这个函数用于删除指定位置的元素。

四、测试代码

cpp 复制代码
void TestList() {
	hbr::list<int> l;

	// 测试push_back和遍历
	for (int i = 0; i < 5; ++i) {
		l.push_back(i);
	}
	for (hbr::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	// 测试push_front
	for (int i = 5; i < 10; ++i) {
		l.push_front(i);
	}
	for (hbr::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	// 测试pop_back
	l.pop_back();
	for (hbr::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	// 测试pop_front
	l.pop_front();
	for (hbr::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	// 测试erase
	hbr::list<int>::iterator it = l.begin();
	++it; ++it; // 让迭代器指向第三个元素
	l.erase(it);
	for (hbr::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	// 测试insert
	it = l.begin();
	++it; // 让迭代器指向第二个元素
	l.insert(it, 100);
	for (hbr::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;
}
相关推荐
面试鸭4 分钟前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展
小白学大数据5 分钟前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
海绵波波1076 分钟前
Webserver(4.9)本地套接字的通信
c++
Python大数据分析@8 分钟前
python操作CSV和excel,如何来做?
开发语言·python·excel
@小博的博客12 分钟前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
上海_彭彭33 分钟前
【提效工具开发】Python功能模块执行和 SQL 执行 需求整理
开发语言·python·sql·测试工具·element
3345543241 分钟前
element动态表头合并表格
开发语言·javascript·ecmascript
沈询-阿里1 小时前
java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl
java·开发语言
AaVictory.1 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
残月只会敲键盘1 小时前
面相小白的php反序列化漏洞原理剖析
开发语言·php