【c++】STL——模拟实现list

一、前言

二、结构分析

节点结构

作为链表,每个位置节点的结构应该设置为结构体:

template<class T>
struct list_node
{
    
    
	list_node(const T& x = T())
		: _next(nullptr)
		, _prev(nullptr)
		, _data(x)
	{
    
    }
	
	list_node<T>* _next;
	list_node<T>* _prev;
	T _data;
};

迭代器结构

我们知道迭代器模拟的是指针的行为,之前在模拟vector的时候,我们将原生指针直接定义为迭代器,因为vector内存连续。但list是链表,且空间不一定连续,没法像vector那样简单粗暴,需要我们对原生指针进行一定的封装。

其中三个类模板分别表示:

T 类型
Ref:T&或 const T&
Ptr :T* 或 const T*

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

我们要模拟原生指针的行为,就要重载几个常用操作符:

*
我们通过拿数据的方式来理解一下为什么要重载 * 操作符以及如何重载 * 操作符

在这里插入图片描述

如图,对于类型T来说,我们迭代器即是模拟T* 拿到 T 的过程。

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

->
对迭代器使用->操作符,返回T类型对象的指针,假如这个对象是一个自定义类型,想要拿到这个对象中的数据就要完成下面的操作

  1. 先对迭代器->拿到指向这个对象的指针。
  2. 对拿到的这个指针->拿到指向空间的数据。
iterator->->_a1;

这样就会连续使用两次->为了可读性,只需要使用一次->即可读取到_a1

这是我认为这里->要返回_data的地址而不是返回具体数据的意义。

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

++/–/==/!=

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& s)
{
    
    
	return _node != s._node;
}

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

List结构

我们发现在这里使用类模板参数会非常的方便构造迭代器和const迭代器。

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;
private:
	node* _head;
}

构造、拷贝构造、赋值重载:

详细的操作与用C语言模拟实现链表几乎一致,忘记可以去翻看我之前讲C语言模拟的文章,唯一需要说明的是,为了与STL保持一致,我们创建的是一个带头双向循环链表,因此在操作前,需要有一个头节点,因此将开辟头节点的任务交给一个函数,保证代码简洁性。

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

void push_back(const T& x)
{
    
    
	node* tail = _head->_prev;
	node* new_node = new node(x);

	tail->_next = new_node;
	new_node->_prev = tail;
	new_node->_next = _head;
	_head->_prev = new_node;
}

list()
{
    
    
	empty_init();
}

//迭代器区间构造
template<class Iterator>
list(Iterator first, Iterator last)
{
    
    
	empty_init();

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

//拷贝构造
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;
}

迭代器:

迭代器这里需要注意,与vector和string的不一样,我们这里是自己定义的一个模仿指针行为的结构体,因此我们返回迭代器的时候需要经历这几个步骤:先使用指针构造一个迭代器对象,在返回时再拷贝构造一份作为返回值,因此我们可以使用匿名对象,编译器会帮助我们将两次构造优化为一次,即直接构造一份返回值。

iterator begin()
{
    
    
	//第一个有效元素
	//使用匿名对象返回比先构造对象再返回好
	return iterator(_head->_next);
}

iterator end()
{
    
    
	//哨兵位头节点
	return iterator(_head);
}

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

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

insert和erase:

非常常规的操作,这里只给出代码:

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

	node* newnode = new node(x);
	newnode->_prev = prev;
	newnode->_next = next;

	prev->_next = newnode;
	next->_prev = newnode;
}

void erase(iterator pos)
{
    
    
	node* prev = pos._node->_prev;
	node* next = pos._node->_next;

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

	delete pos._node;
}

析构与clear():

析构时同样要与vector、string作好区分,list的迭代器在erase后,就会立刻失效,我们可以使用后置++巧妙解决这个问题,因为后置++返回的是一份迭代器的拷贝,真正的迭代器已经完成了++操作。

void clear()
{
    
    
	iterator it = begin();
	while (it != end())
	{
    
    
		//后置++,erase的是tmp,迭代器已经是下一个了
		erase(it++);
	}
}

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

三、反向迭代器

template<class Iterator,class Ref,class Ptr>
struct ReverseIterator
{
    
    
	typedef ReverseIterator<Iterator,class Ref, class Ptr> Self;
	Iterator _cur;

	ReverseIterator(Iterator it)
		:_cur(it)
	{
    
     }

	Ref operator*()
	{
    
    
		Iterator tmp = _cur;
		--tmp;
		return *tmp;
	}

	Self& operator++()
	{
    
    
		--_cur;
		return *this;
	}

	Self& operator--()
	{
    
    
		++_cur;
		return *this;
	}

	bool operator!=()
	{
    
    
		return _cur != s._cur;
	}

};

四、结语

以上就是本篇文章的全部内容了,我们下次再见!

猜你喜欢

转载自blog.csdn.net/m0_73209194/article/details/129544061