STL详解——list的模拟实现

发布于:2025-06-09 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

1.list的定义

2.迭代器

2.1基本功能实现

2.2const迭代器

2.2.1基本迭代器的完善

2.2.2const迭代器的基本实现

2.2.3模板优化语法

2.3自定义类型数据迭代器

2.3.1使用情景

2.3.2嵌套类模板

3.其他接口的实现

3.1insert与erase

3.2头插与头删

3.3析构与拷贝构造

3.4赋值运算符重载与构造扩展


1.list的定义

STL中的list容器不同于数据结构中常见的单链表,而是一种带头双向链表,因此在实现过程中也以此结构为基础实现。对于链表,最基本的便的其结点,因此我们先实现结点的定义:

template<class T>
struct _list_node
{
	T val;
	_list_node* next;
	_list_node* prev;
};

 完成结点的定义后,我们便成将list容器抽象为以头结点(哨兵位)为头的多结点链,结点间能通过彼此互相访问。因此list的成员变量便是头结点(_head)。

对list进行初始化,因是带头双向链表,因此初始化就是将头结点初始化为一个next与prev都指向头的结点,但在这之前需要给_head申请(new)一个结点(Node)的空间,new自定义类型的时候会调用结点类的构造函数,因此这时还需要完善结点类的构造函数

//结点
template<class T>
struct _list_node
{
	T val;
	_list_node* next;
	_list_node* prev;

	//完善构造
	_list_node()
		:val(0)
		,next(nullptr)
		,prev(nullptr)
	{

	}
};

//list链表
template<class T>
class list
{
public:
	typedef _list_node<T> Node;

	//构造
	list()
	{
		_head = new Node;
		_head->next = _head;
		_head->prev = _head;
	}

private:
	Node* _head;
};

2.迭代器

2.1基本功能实现

迭代器的基本功能就是通过类指针的形式来遍历容器元素,对于vector这种底层为数据的容器,可以用最简单的指针来完成迭代器的封装,而list这种底层非连续空间的容器,则需要将遍历的方式封装为一种迭代器类。初步接触迭代器封装,可以通过使用来逐步实现:

namespace my_list
{
	void test01()
	{
		list<int> lt1;
		list<int>::iterator it = lt1.begin();
		while (it != lt1.end())
		{
			cout << *it << ' ';
            ++it;
		}
		cout << endl;
	}
}

迭代器最基本的使用无非就是要完成上面的功能,list最基本元素是结点,因此迭代器成员变量代表的也应是结点, 接下来要实现的便是++迭代器能完成指向下一个结点,迭代器解引用能返回结点中的值。代码如下:

	//迭代器(一、初代)
	template<class T>
	struct _list_iterator
	{
		typedef _list_node<T> Node;
        Node* _node;
		
		_list_iterator(Node* node)
			:_node(node)
		{

		}

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

		_list_iterator<T> operator++()
		{
			_node = _node->next;
			return *this;
		}

		bool operator!=(const _list_iterator<T>& it)//注意不能使用非const引用
		{
			return _node != it._node;
		}
	};

	//list链表
	template<class T>
	class list
	{
	public:
		typedef _list_node<T> Node;
		typedef _list_iterator<T> iterator;

		//构造
		list()
		{
			_head = new Node;
			_head->next = _head;
			_head->prev = _head;
		}

		//迭代器
		iterator begin()
		{
			return iterator(_head->next);
		}

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

	private:
		Node* _head;
	};

这里需要注意的是begin与end的返回值为临时对象(具有常性)不能用非const引用来接受,因此在!=重载函数中的参数选择为const引用。

下面通过push_back插入数据来检验一下:

void push_back(const T& x)
{
	Node* newnode = new Node;
	newnode->val = x;
	newnode->next = _head;
	newnode->prev = _head->prev;
	_head->prev->next = newnode;
	_head->prev = newnode;
}
namespace my_list
{
	void test01()
	{
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.push_back(5);
		list<int>::iterator it = lt1.begin();
		while (it != lt1.end())
		{
			cout << *it << ' ';
			++it;
		}
		cout << endl;
	}
}

int main()
{
	my_list::test01();

	return 0;
}

运行结果如下:

这里也算基本完成迭代器功能。

2.2const迭代器

2.2.1基本迭代器的完善

在完成const迭代器前,先对之前的基本迭代器进行完善,即完善相对应的运算符重载。

_list_iterator<T> operator++(int)
{
	_list_iterator<T> tmp(*this);
	_node = _node->next;
	return *this;
}

_list_iterator<T>& operator--()
{
	_node = _node->prev;
	return *this;
}

_list_iterator<T> operator--(int)
{
	_list_iterator<T> tmp(*this);
	_node = _node->prev;
	return tmp;
}

bool operator== (const _list_iterator<T>& it)
{
	return _node == it._node;
}

2.2.2const迭代器的基本实现

const迭代器的功能是迭代器可以修改,但让迭代器指向的内容不能修改。因此这里的迭代器不能像vector中的直接在iterator前加const修饰,因为vector中迭代器底层是指针,const修饰的实际是指针类型;而list中迭代器是一种自定义类型,使用const修饰的效果为迭代器不能修改,但迭代器指向的内容可以修改。

因此这里要实现const迭代器功能主要是让解引用返回的对象不能修改,也就是让返回值变为const T&即可。

但这里的iterator需变为const_iterator,类的类型发生了改变,因此这里需要再重新创建一个const迭代器类:

//二、const迭代器
template<class T>
struct _list_iterator
{
	typedef _list_node<T> Node;
	Node* _node;

	_list_iterator(Node* node)
		:_node(node)
	{

	}

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

	_list_iterator<T> operator++()
	{
		_node = _node->next;
		return *this;
	}

	bool operator!=(const _list_iterator<T>& it)
	{
		return _node != it._node;
	}

	_list_iterator<T> operator++(int)
	{
		_list_iterator<T> tmp(*this);
		_node = _node->next;
		return *this;
	}

	_list_iterator<T>& operator--()
	{
		_node = _node->prev;
		return *this;
	}

	_list_iterator<T> operator--(int)
	{
		_list_iterator<T> tmp(*this);
		_node = _node->prev;
		return tmp;
	}

	bool operator== (const _list_iterator<T>& it)
	{
		return _node == it._node;
	}
};

template<class T>
struct _list_const_iterator
{
	typedef _list_node<T> Node;
	Node* _node;

	_list_const_iterator(Node* node)
		:_node(node)
	{

	}

	_list_const_iterator(const _list_iterator<T>& it)
		:_node(it._node)
	{ }

	const T& operator*()
	{
		return _node->val;
	}

	_list_const_iterator<T> operator++()
	{
		_node = _node->next;
		return *this;
	}

	bool operator!=(const _list_const_iterator<T>& it)
	{
		return _node != it._node;
	}

	_list_const_iterator<T> operator++(int)
	{
		_list_const_iterator<T> tmp(*this);
		_node = _node->next;
		return *this;
	}

	_list_const_iterator<T>& operator--()
	{
		_node = _node->prev;
		return *this;
	}

	_list_const_iterator<T> operator--(int)
	{
		_list_const_iterator<T> tmp(*this);
		_node = _node->prev;
		return tmp;
	}

	bool operator== (const _list_const_iterator<T>& it)
	{
		return _node == it._node;
	}
};

list中迭代器相关函数const重载如下:

//迭代器
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 const_iterator(_head);
}

在const迭代器类中为支持非const迭代器转换为const迭代器,重载了使用迭代器初始化的拷贝构造。

这里能不能将代码进行简化呢?

2.2.3模板优化语法

const迭代器仅仅是在解引用时返回值不同,按前面写法便要写两个类。若这时将解引用返回的类型用模板表示,这时通过不同的模板实例化也能区分两种类。具体实现方法如下:

//二、const迭代器
template<class T, class Ref>
struct _list_iterator
{
	typedef _list_node<T> Node;
	typedef _list_iterator<T, Ref> Self;

	Node* _node;

	_list_iterator(Node* node)
		:_node(node)
	{

	}

	_list_iterator(const _list_iterator<T, T&>& it)
		:_node(it._node)
	{

	}

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

	Self operator++()
	{
		_node = _node->next;
		return *this;
	}

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

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

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

这样通过模板实例化的不同也能实现const迭代器与普通迭代器的分离。

2.3自定义类型数据迭代器

2.3.1使用情景

list作为一种顺序容器,除储存内置类型外,也可储存自定义类型,如储存位置数据(Pos):

struct Pos
{
	int _row;
	int _col;

	Pos(int row = 0, int col = 0)
		:_row(row)
		,_col(col)
	{

	}
};

void test02()
{
	list<Pos> lt1;
	list<Pos>::iterator it = lt1.begin();
	while (it != lt1.end())
	{

	}
}

对于这种自定义类型,重载的解引用出来是Pos结构体,要访问其中数据还要自己处理:

void test02()
{
	list<Pos> lt1;
	lt1.push_back({ 1,1 });
	lt1.push_back({ 2,2 });
	lt1.push_back({ 3,3 });
	list<Pos>::iterator it = lt1.begin();
	while (it != lt1.end())
	{
		cout << (*it)._row << ':' << (*it)._col << endl;
		++it;
	}
}

但在STL库中,迭代器对于这种自定义类型还提供了类似结构体指针的访问方法:

但对于我们自己的list容器,迭代器并不是指向该自定义类型指针,因此这里实际是对->运算符的重载。->的重载实际是要返回Pos的指针,这里为了区分const与非const,依旧使用模板优化语法。 

template<class T, class Ref, class Ptr>
struct _list_iterator
{
	typedef _list_node<T> Node;
	typedef _list_iterator<T, Ref, Ptr> Self;

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

但这里需要注意:->重载后返回的是结构体的指针,即整个it->代表的是结构体指针,原则上需要在it->后再使用->调用数据,但编译器简化成了一个->。

2.3.2嵌套类模板

先来看看下面场景:

template <class T>
void Print(const list<T>& lt)
{
	list<T>::const_iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << ' ';
        ++it;
	}
	cout << endl;
}

运行结果如下:

可以看到这种写法是错误的,原因是在类模板未实例化时,不能去找类模板后面东西,编译器分不清const_iterator是嵌套内类,还是静态成员变量。这时可以用typename告诉编译器这里是类型:

template <class T>
void Print(const list<T>& lt)
{
	typename list<T>::const_iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << ' ';
	}
	cout << endl;
}

当然也可以用auto接收迭代器:

auto it = lt.begin();

3.其他接口的实现

在实现完迭代器后,便能接着实现许多依托于迭代器功能的接口。

3.1insert与erase

insert与erase接口实现逻辑类似:

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

iterator erase(const iterator& pos)
{
	Node* next = pos._node->next;
	Node* prev = pos._node->prev;

	next->prev = prev;
	prev->next = next;

	delete pos._node;
	return iterator(next);
}

3.2头插与头删

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

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

3.3析构与拷贝构造

void empty_init()
{
	_head = new Node;
	_head->next = _head;
	_head->prev = _head;
}

//构造
list()
{
	empty_init();
}

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

	const_iterator it = lt.begin();
	while (it != lt.end())
	{
		push_back(*it);
		++it;
	}
}

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

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

3.4赋值运算符重载与构造扩展

list(std::initializer_list<T> il)
{
	empty_init();
	for (const auto& e : il)
	{
		push_back(e);
	}
}

//赋值重载
void swap(list<T>& lt)
{
	if (this != &lt)
	{
		std::swap(_head, lt._head);
	}
}

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


网站公告

今日签到

点亮在社区的每一天
去签到