C++修炼之路之list模拟实现--C++中的双向循环链表

目录

引言 

一:STL源代码中关于list的成员变量的介绍

二:模拟实现list

1.基本结构

2.普通迭代器 +const迭代器的结合

3.构造+拷贝构造+析构+赋值重载 +清空

4.insert+erase+头尾插入删除 

 5.打印不同数据类型的数据《使用模板加容器来完成》

三:全部代码加测试用例链接 

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧

引言 

在前面的数据结构中已经实现了c版本的list-双向循环链表,但在c++中注重的是分装,对于操作进行封装处理,对于我们使用便是方便了不少,但模拟实现的话还是有许多要注意的细节点,尤其是list的迭代器较复杂,需要认真理解

一:STL源代码中关于list的成员变量的介绍

在源代码中使用一个头结点的指针来 模拟实现的,但在文档中只介绍list是一个双向链表的容器,并没有说明是否带哨兵位了,之时就得观察初始化函数和构造函数等来观察

 所以从这里就可以看出list的结构就是一个带头循环双向链表,接下来我们就开始模拟实现

二:模拟实现list

1.基本结构

template<class T>//泛型编程
struct list_node
{
	T _data;
	list_node<T>* _prev;
	list_node<T>* _next;

	list_node(const T &x=T())//缺省值为匿名对象
		:_data(x)
		,_prev(nullptr)
		,_next(nullptr)
	{}
};

template<class T>
class list
{
	typedef list_node<T> node;
public:

private:
	node* head;
	size_t size;//方便size()接口,省去便利的开销
};

2.普通迭代器 +const迭代器的结合

对于用迭代器来遍历list的数据,就不能像vector和string那样,直接使用,因为

list<int>::iterator it=lt1.begin();

while(it!=lt1.end())

{

     cout<<*it<<" ";

     ++it;

}

cout<<endl;

对于这里面的*it和++it和it!=lt1.end()这些操作是不能直接支持的,因为*it是要取data的数据,++it就是要使_node=_node->next等,这些是和vector,string是不一样的,所以得自己手动支持,运用运算符重载+函数封装,我们先来观察源代码中的实现

我们先来实现普通迭代器

 

template<class T>
struct _list_iterator
{
	typedef list_node<T> Node;
	typedef _list_iterator<T> self;
	Node* node;

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

	self& operator++()
	{
		_node = _node->next;
		return *this;
	}
	self& operator--()
	{
		_node = _node->prev;
		return *this;
	}
	self operator++(int)
	{
		self tmp(*this);
		_node = _node->next;
		return tmp;
	}
	self operator--(int)
	{
		self tmp(*this);
		_node = _node->prev;
		return tmp;
	}
	T& operator*()
	{
		return _node->data;
	}
	T* operator->()
	{
		return &_node->data;
	}
	bool operator!=(const self& s)
	{
		return _node != s._node;
	}
	bool operator==(const self& s)
	{
		return _node == s._node;
	}

};
template<class T>
class list
{
	typedef list_node<T> node;
public:
	typedef _list_iterator<T> iterator;
	iterator begin()
	{
		//return iterator(_head->_next);
		return _head->_next;
	}
	iterator end()
	{
		return _head;
	}

对于这个结构可以这样来理解

 

 我们知道const对象使用的是const迭代器那么在iterator之前加const的话,这样const iterator指的是迭代器本身不能修改,但在遍历过程迭代器本身是要改变的,如++it等,所以不能这样写

应该为const_iterator这是一个重新定义的一个类型,要做到迭代器本身可以改变,但迭代器指向的内容不能改变

在原来普通迭代器的基础上重载出一份const迭代器的版本如

但这样的话对于普通迭代器和const迭代器两份代码是由许多相同的代码的,比较冗余,所以在源代码中提出了一份更好的解决方案来解决这个问题 

在原来基础上多添加两个参数,这样对于同一个类模板,实例化参数不同就是不同的类型

对于上面的代码就要优化为更完善的代码,展示主要优化的代码

 

template<class T,class Ref,class Ptr>
struct _list_iterator
{
	typedef list_node<T> Node;
	typedef _list_iterator<T,Ref,Ptr> self;
	Node* node;
	
	Ref operator*()
	{
		return _node->data;
	}
	Ptr operator->()
	{
		return &_node->data;
	}
	
	typedef _list_iterator<T,T&,T*> iterator;
	typedef _list_iterator<T, const T&, const T*> const_iterator;
	//typedef __list_const_iterator<T> const_iterator;
	iterator begin()
	{
		//return iterator(_head->_next);
		return _head->_next;
	}
	iterator end()
	{
		return _head;
	}
	const_iterator begin()const
	{
		//return iterator(_head->_next);
		return const_iterator(_head->_next);
	}
	const_iterator end()const
	{
		return const_iterator(_head);
	}

对于->编译器是做处理的,将两个->优化为一个详细看代码及测试 用例3中

3.构造+拷贝构造+析构+赋值重载 +清空

构造时要使用带头循环双向循环链表的初始化操作,观看https://blog.csdn.net/Miwll/article/details/136593441?spm=1001.2014.3001.5502

 

void empty_init()
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;
}
list()
{
	empty_init();
}
list(const list<T>& t)//拷贝构造实现为深拷贝
{
	empty_init();
	for (auto e : t)
	{
		push_back(e);
	}
}
/*list<int>& operator=(const list<int>& lt)
{
	if (this != &lt)
	{
		clear();
		for (auto e : lt)
		{
			push_back(e);
		}
	}
	return *this;
}*/
void swap(list<T>& t)
{
	std::swap(_head,t._head);
	std::swap(_size, t._size);
}
list<int>& operator=(const list<int>& lt)
{
	swap(lt);
	return *this;
}
~list()
{
	clear();
	delete _head;
	_head =nullptr;
}
void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
}

4.insert+erase+头尾插入删除 

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

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

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

void pop_back()
{
	erase(--end());
}
iterator insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* newnode = new Node(x);
	Node* prev = cur->_prev;

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

	delete cur;
	prev->_next = next;
	next->_prev = prev;
	--_size;
	return iterator(next);
}
size_t size()
{
	return _size;
}

 5.打印不同数据类型的数据《使用模板加容器来完成》

使用模板来打印自定义类型数据,注意这里加的typename

list<T>未实例化的类模板,编译器不能去他里面去找
编译器就无法list<T>::const_iterator是内嵌类型,还是静态成员变量
前面加一个typename就是告诉编译器,这里是一个类型,等list<T>实例化
再去类里面去取

三:全部代码加测试用例链接 

https://gitee.com/lin-ciyu/cplusplus/tree/master/my_list/my_list

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值