C++程序设计--- STL list的模拟实现

前言

这次我们来实现STL List的模拟实现,首先明白STL的list底层是双向带头循环链表。模拟实现list与之前一样先从成员变量,再到四大天选之子,最后到有各种功能成员函数, 最后再到迭代器,由于List的物理结构与前面vector和string不同,使得迭代器也完全不同。由于代码比较多,我把需要注意的地方在代码中以注释的形式写出来。


成员变量

因为list的物理结构不再是连续的物理空间,而是变成一个又一个的结点,导致成员变量从指针变成了类。

//结点定义
template <class T>
struct _list_node
{
	_list_node<T>* _prev = nullptr;
	_list_node<T>* _next = nullptr;
	T _data;
	//缺省值给T()考虑到模板参数不仅仅是内置类型,缺省值要给0不合适,不给缺省值编译器会报错。因此采用匿名对象,从而调用匿名对象的默认构造。
	_list_node(const T& val = T())
		:_data(val)
	{}
};
//list的成员变量
private:
	node* _head = nullptr;

天选之子

构造函数

//许多地方都需要调用初始化,因此我单独将其写为一个函数
void empty_init()
{
	_head = new node;
	_head->_prev = _head;
	_head->_next = _head;
}

void swap(list<T>&tmp)
{
	std::swap(_head, tmp._head);
}
//无参构造
list()
{
	empty_init();
}
//区间构造
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
	empty_init();
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

拷贝构造
现代写法:

list(const list<T>&x)
{
	empty_init();
	list tmp(x.begin(), x.end());
	swap(tmp);
}

ps: 什么是现代写法
以往的写法里面开空间、拷贝等等一类操作都是由程序猿一步步来调用new memcpy等来完成的,而现代写法则是调用前面已经实现的函数从而实现,大大降低代码量, 一旦出现问题只需要修改一处,从而修复其他地方的问题。

赋值重载
现代写法:

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

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

析构函数

//list有一个功能是clear,清除list除头结点的函数。析构函数只需要调用其函数,再将头结点释放掉就完成了。
//迭代器的问题后面提
void clear()
{
	iterator it = begin();
	while (it != end())
	{
		erase(it++);
	}
}

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

迭代器

list的迭代器与string和vector的迭代器完全不同,且是list中我认为最精华的东西。由于list的底层并不是连续的物理空间,因此SGI版本的采用原生指针作为迭代器,变得行不通了。因此SGI中采用类去模拟指针的行为
实现方法如下`

//为什么模板类型会有什么Ref、Ptr
//原因是实现const_iterator,又需要将iterator CV一份,而改动的地方只有operator* operator->的返回值。这样代码显得十分冗余。因此通过模板来改变这样的情况,请看下面的图,能够更好的理解这一块。
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* ptr)
		:_node(ptr)
	{}

	_list_iterator(const self& v)
	{
		_node = v._node;
	}

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

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

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

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

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

//list中的迭代器部分

template<class T>
class list
{
public:
	typedef _list_node<T> node;
	typedef _list_iterator<T, T&, T*> iterator;
	typedef _list_iterator<T, const T&, const T*> const_iterator;
	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);
	}
}

在这里插入图片描述

成员函数

每个成员函数要注意的 容易出错的地方都写在注释里

修改函数


//1、prev记录pos的prev、next记录pos结点
//2、创建新的结点
//3、两两结点连接起来
void insert(iterator pos, const T& val)
{
	node* prev = pos._node->_prev;
	node* next = pos._node;

	node* new_node = new node(val);
	//node* new_node = new node;
	//new_node->_data = val;
	new_node->_prev = prev;
	prev->_next = new_node;
	new_node->_next = next;
	next->_prev = new_node;
}

//1、检查pos位置是否合法也就是不等于head
//2、记录pos位置的prev和next、pos的结点
//3、将pos的prev结点和next结点连接起来
//4、把pos结点释放掉
iterator erase(iterator pos)
{
	assert(pos._node != _head);
	node* prev = pos._node->_prev;
	node* next = pos._node->_next;
	node* current_node = pos._node;

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

	return iterator(next);
}
//复用insert和erase即可
void push_back(const T& val)
{
	insert(end(),val);
}

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

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

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

总结

list的insert erase等功能函数实现起来还是比较常规,属于链表的增删问题。但是list的迭代器绝对是将模板和类玩到了新的高度,很值得我们去学习,以及学习这样的思想,不仅仅是停留在简单粘贴复制一份代码,再轻微的改动,这样使得代码显得十分冗余。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值