C++_适配器模式——reverse_iterator模拟实现

本文介绍了C++中的适配器模式,特别是如何在STL中应用这一模式来实现如reverse_iterator的功能。适配器允许不同的接口协同工作,提供了代码复用的手段。文中通过模拟实现stack和queue展示了容器适配器的工作原理,并详细解释了如何设计和使用反向迭代器reverse_iterator,强调了其与正向迭代器的关系和操作差异。
摘要由CSDN通过智能技术生成


前言

本篇博客主要会给大家讲解C++的一个代码复用的重要方式——适配器模式,并且详细讲解stl是如何运用这中设计理念来实现reverse_iterator的,给出了模拟实现方式

适配器(Adapter)

作为stl六大组件之一,适配器也是stl中重要的一部分,那么,适配器是什么呢?

适配器就是接口,对容器、迭代器、算法进行包装,但其实质还是容器、迭代器和算法,只是不依赖于具体的标准容器、迭代器和算法类型。概念源于设计模式中的适配器模式:将一个类的接口转化为另一个类的接口,使原本不兼容而不能合作的类,可以一起运作。

适配器模式的设计模式的出现,为开发带来了极大的便利,提供了一个极好的代码复用的手段,在stl中,适配器主要分为三类:容器适配器,迭代器适配器,仿函数适配器,本篇博客将简单的介绍容器适配器迭代器适配器

容器适配器

应用于容器,容器适配器包括:stack,queue,priority_queue
我们就拿stack和queue来举例,首先我们可以先看看c++文档中对这两个适配器的声明
在这里插入图片描述
在这里插入图片描述

这两个适配器容器相对于普通容器来说最大的区别就是用了第二个模板参数,而这个模板参数的默认参数是deque容器,也就是说这两个容器是在模板提供的容器的前提下进行的设计,也就是说传入什么容器就会生成底层是什么容器的栈和队列,如下:

#include<stack>
using namespace std;
int main()
{
	//默认生成用deque设立的栈
	stack<int> st1;
	//生成底层为vector的栈
	stack<int, vector<int>> st2;
	return 0;
}

那么具体是怎么实现的呢,可以看下面模拟实现的代码:

//模拟实现stl_queue

#pragma once
#include<deque>

namespace lzz
{
	template<typename T, typename container = deque<T>>
	class queue
	{
	private:
		container _con;
	public:
		queue() {}
		void push(const T& val) { _con.push_back(val); }
		void pop() { _con.pop_front(); }
		T& back() { return _con.back(); }
		const T& back() const { return _con.back(); }
		T& front() { return _con.front(); }
		const T& front() const { return _con.front(); }
		size_t size() const { return _con.size(); }
		bool empty() const { return _con.empty(); }
	};
}

///
//模拟实现stl_stack
#pragma once
#include<deque>

namespace lzz
{
	template<typename T, typename container = std::deque<T>>
	class stack
	{
	private:
		container _con;
	public:
		//自定义类型自动调用构造和析构
		stack() {}
		void push(const T& val) { _con.push_back(val); }
		void pop() {_con.pop_back(); }
		T& top() { return _con.back(); }
		size_t size() { return _con.size(); }
		bool empty() { return _con.empty(); }
	};
}

通过模拟实现,我们就可以发现其实容器适配器就是利用stl的容器接口再次进行上层封装得来的,通过上层封装能够做到忽略一些底层的细节,实现代码复用。

迭代器适配器——reverse_iterator

reverse_iterator,故名思意就是反向迭代器,大家仔细了解就能够知道,反向迭代器的++就是正向迭代器的–,反向迭代器的–就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即使用适配器模式进行设计,即:反向迭代器内部都可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。

首先,我们先来分析一下如何设计reverse_iteratorconst_reverse_iterator,需要写两份代码吗?首先我们来回顾一下iteratorconst_iterator是如何设计的:

template<typename T, typename Ref, typename Ptr>
struct __list_iterator
{
	//...
}

具体可以看下面的文章链接:
stl_list模拟实现
其中有具体的iterator的设计思路和代码

是通过传三个参数来进行控制的,所以在设计reverse_iterator时,我们也可以采取这种方式,下面是reverse_iterator的整体框架:

	template<typename Iterator, typename Ref, typename Ptr>
	class Reverse_iterator
	{
	//...
	}
	//通过如此设计反向迭代器,所有容器的反向迭代器都可以通过传入对应正向迭代器并进行typedef实现
	//list
	typedef Reverse_iterator<__list_iterator, T&, T*> reverse_iterator
	typedef Reverse_iterator<__const_list_iterator, T&, T*> const_reverse_iterator
	//
	//vector
	typedef Reverse_iterator<__vector_iterator, T&, T*> reverse_iterator
	typedef Reverse_iterator<__const_vector_iterator, T&, T*> const_reverse_iterator

具体逻辑

stl在设计反向迭代器时,采用了镜像对称的方式进行设计,也就时说,rbegin()指向的地方就是end()指向的地方,rend()指向的地方就是begin()指向的地方。

在这里插入图片描述
但是这也出现了一个问题,如果解引用,由于镜像对称的原因如果按照正常的解引用操作拿到的数据是错误的,需要拿到前一个迭代器指向的数据才能正确对应关系,所以reverse_iterator的operator*()和operator->()函数需要重新设计,如下:

		//其中先构造一个tmp的原因是不能直接修改_it,而要获得前一个数据只能先创建一个临时变量
		Ref& operator*() 
		{
			Iterator tmp(_it);
			return *--tmp;
		}
		Ptr operator->()
		{
			Iterator tmp(_it);
			--tmp;
			return &(tmp.operator*());
		}	

接着对反向迭代器进行++就是–,对正向迭代器进行–就是++,具体逻辑就是这样了。

完整代码

#pragma once

namespace lzz
{
	template<typename Iterator, typename Ref, typename Ptr>
	class Reverse_iterator
	{
	private:
		Iterator _it;
		typedef Reverse_iterator<Iterator, Ref, Ptr> self;
	public:
		Reverse_iterator() {}
		Reverse_iterator(const Iterator& it) { _it = it; }
		//由于使用了镜像对称,所以应该是返回他的上一个数据
		Ref& operator*() 
		{
			Iterator tmp(_it);
			return *--tmp;
		}
		Ptr operator->()
		{
			Iterator tmp(_it);
			--tmp;
			return &(tmp.operator*());
		}
		self& operator++()
		{
			--_it;
			return *this;
		}
		self& operator--()
		{
			++_it;
			return *this;
		}
		bool operator!=(const self& rit) { return _it != rit._it; }
	};
}

总结

通过这篇博客,相信大家对适配器模式的这种设计思路有了一定的理解,当然若要想有更深的理解,离不开不断的实践!本篇博客到此结束,如果博主有一些错误或者大家有不懂的地方欢迎大家评论区指出!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暮雨清秋.L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值