通过设计list类深入理解iterator迭代器


title: 通过设计list类深入理解iterator迭代器
date: 2024-04-26 16:10:11
tags: C++ 类和对象
cover: https://picbed0521.oss-cn-shanghai.aliyuncs.com/blogpic/Date.jpg

原文及更多原创文章见我的网站: www.supdriver.top

前置博客:从构建一个Date类入门C++类与对象
设计模式介绍:TODO

Iterator迭代器实际上也是一种设计模式,它提供了一种方法顺序访问一个聚合对象中各个元素

下面先迅速地搓一个list

template <class T>//先用模板创建一个节点类
struct ListNode
{
	T _val;
	ListNode<T>* _next;
	ListNode<T>* _prev;

	//提供全缺省的默认构造函数
	ListNode(const T& val = T()) :_val(val), _next(nullptr), _prev(nullptr) {}
};

//用ListNode构造list类

template <class T>
class list
{
	typedef ListNode<T> Node;//用typedef简化代码
public:
	list()//默认构造函数
	{
		_head = new Node;
		//维护两个指针
		_head->_next = _head;
		_head->_prev = _head;
	}

	void push_front(const T& val)//头插
	{
		Node* newnode = new Node(val);
		Node* next = _head->_next;//额外的指针,简化代码

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

	void pop_front()//尾插
	{
		if (empty()) return;

		Node* cur = _head->_next;
		
		_head->_next = cur->_next;
		cur->_next->_prev = _head;
		delete cur;
	}

	bool empty() //判空
	{
		return _head->_next == _head;
	}

	//剩余代码自行补全
    //......
private:
	Node* _head;
};

list的迭代器

不同于vector底层的数据在内存中连续存储,可以用原生指针充当迭代器,例如typedef T* iterator

list的底层是链表,在内存中分散存储,是不能原生指针连续*访问的,所以为了解决这一复杂问题,
需要自己写一个iterator

普通迭代器

template<class T>//迭代器也得用模板
struct __list_iterator
{
	typedef __list_iterator<T> Self;//简化代码
	typedef ListNode<T> Node;
	Node* _node;

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

	Self& operator++()//重载operator++
	{
		_node = _node->_next;
		return *this;
	}

	bool operator!=(const Self& it) const //重载!==,比较操作符记得加const
	{
		return _node != it._node;
	}

	T& operator*()//重载 *
	{
		return _node->_val;
	}
};

list类中添加如下代码

public:
	typedef __list_iterator<T> iterator;

    iterator begin()
	{
		return iterator(_head->_next);
	}

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

通过如上修改,list已经支持普通迭代器,并且非const修饰的list已经支持范围for

测试代码如下

int main()
{
	list<int> lst;
	lst.push_front(5);
	lst.push_front(4);
	lst.push_front(3);
	lst.push_front(2);
	lst.push_front(1);

	for (auto e : lst)
	{
		cout << e << " ";
	}
	return 0;
}

const迭代器

const list要能提供const_iterator,因此我们还要写一个const_iterator类…吗?

其实并不用,要利用好C++中的模板语法来大大提高代码的复用性,尤其像iteratorconst_iterator这种差别不大的类,没必要每个都单独写一段代码

为此我们的__list_iterator只需要能用模板解决好二者的差异即可。而目前最大的问题是什么?是operator*()的返回值问题,一个是返回T&,另一个是const T&,其他的成员函数则基本没差别,所以不妨扩充一下模板参数,添加一个Ref类。

有变化的代码如下

template<class T,class Ref>//增加一个Ref参数
struct __list_iterator
{
	typedef __list_iterator<T,Ref> Self;//Self这里的原类也要加
	typedef ListNode<T> Node;
	Node* _node;

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

	Ref operator*()//直接返回Ref类
	{
		return _node->_val;
	}
};

list类也有相应变化

public:
	typedef __list_iterator<T,T&> iterator;//普通迭代器
	typedef __list_iterator<T,const T&> const_iterator;//const迭代器

    const_iterator begin() const //针对const指针的
	{
		return const_iterator(_head->_next);
	}

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

这样通过增加一个Ref模板参数,完成了对iteratorconst_iterator的代码级统一(当然模板实例化出来是不一样的)

但别忘了迭代器还要提供->操作符的重载,而operator->()函数要返回不同的指针,所以我们如法炮制,再增加一个Ptr模板参数

有变化的代码如下

template<class T,class Ref,class Ptr>//增加一个Ptr参数
struct __list_iterator
{
	typedef __list_iterator<T,Ref,Ptr> Self;//Self相应更改

	Ptr operator->()//重载->
	{
		return &(_node->_val);
	}
};

list类也有相应变化

public:
	typedef __list_iterator<T,T&,T*> iterator;//普通迭代器
	typedef __list_iterator<T,const T&,const T*> const_iterator;//const迭代器

至此,list__list_iterator的基本功能已基本完成,本篇的重点__list_iterator主要解决了两点问题

-为了应对list的迭代器的复杂性,单独为其构建一个__list_iterator类,并提供一系列的操作符重载
-为了提高代码的复用性,仅用一个__list_iterator类来typedef普通迭代器和const迭代器,我们增加了模板参数,最终模板变为template<class T, class Ref, class Ptr>

用普通迭代器构造const迭代器

有时候我们需要用普通迭代器构造const迭代器,于是可以给__list_iterator提供一个比较有意思的构造函数,
可以实现时而充当拷贝构造,时而充当满足上述的构造

代码如下

typedef __list_iterator<T,Ref,Ptr> Self;//再展示一遍Self的代码,便于下文对比
typedef __list_iterator<T,T&,T*> iterator;//指定普通迭代器,并用typedef简化代码

__list_iterator(iterator it) :_node(it._node) {}

-当模板参数为<T,T&,T*>时,Selfiterator相同,上段代码中的构造函数相当于拷贝构造

-当模板参数为<T,const T&,const T*>时,Selfiterator不同,Slefconst迭代器,iterator始终是普通迭代器,这个构造函数便能用普通迭代器构造const迭代器

小结

通过构造一个list类,我们使用到了更复杂的迭代器,使用了带3个模板参数__list_iterator类定义普通迭代器和const迭代器,学习了如何利用模板参数提高代码的复用性,如何提供额外的构造函数使__list_iterator支持用普通迭代器构造const迭代器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值