Bootstrap

queue的模拟实现【C++】

全部的实现代码放在了文章末尾

queue的模拟实现和stack一样,采用了C++适配器模式

queue的适配器一般是deque,也可以是list

因为queue是有特殊限制的线性表【只能在队头删除,队尾插入】,所以只要是线性结构并且可以高效的实现头插和尾删的线性表,就都可以作为queue的适配器


什么是适配器模式?

适配器模式是一种设计模式,它允许将不兼容接口的类一起工作

适配器模式通常用于以下情况:

  1. 希望使用一个类,但其接口与其他代码不兼容。
  2. 希望创建一个可重用的类,它能够将接口转换为其他接口。
  3. 希望使用第三方库或遗留代码,但其接口与其他代码不兼容。

适配器模式通常包括以下三个主要部分:

  1. 目标接口(Target):这是期望使用的接口,客户端代码只能与目标接口交互。
  2. 源接口(Adaptee):这是需要适配的类,其接口与目标接口不兼容。
  3. 适配器(Adapter):这是一个类,它实现了目标接口,并将调用转换为对源接口的调用。适配器将源接口的调用转换为目标接口的调用,使得客户端代码可以与目标接口交互。

可以类比我们生活中的家庭电源接口笔记本电脑充电口电源适配器,它们之间也是一种适配器关系

笔记本电脑充电口是上面提到的目标接口
家庭电源接口是上面提到的源接口
电源适配器是上面提到的适配器

笔记本电脑的充电口是不能和家庭电源接口直接连接进行充电的,因为笔记本电脑用的是直流电,而家庭电源输出的是交流电,所以要把交流电转换为直流电才能给笔记本电脑供电,而电源适配器就能做到这一点

对应了上面提到的适配器模式解决的问题:
可以将不兼容接口的类一起工作


准备工作

创建两个文件,一个头文件myqueue.hpp,一个源文件test.cpp

【因为模板的声明和定义不能分处于不同的文件中,所以把成员函数的声明和定义放在了同一个文件myqueue.hpp中】

  1. myqueue.hpp:存放包含的头文件,命名空间的定义,成员函数和命名空间中的函数的定义

  2. test.cpp:存放main函数,以及测试代码


包含头文件

  1. iostream:用于输入输出

  2. list:提供list类型的适配对象

  3. deque: 提供deque类型的适配对象


定义命名空间

在文件myqueue.hpp中定义上一个命名空间myqueue
把stack类和它的成员函数放进命名空间封装起来,防止与包含的头文件中的函数/变量重名的冲突问题


类的成员变量

只有一个,是适配器对象,默认con是deque类型

在这里插入图片描述


默认成员函数

stack类的模拟实现的时候一样
queue的四大默认成员函数:构造函数,拷贝构造函数,析构函数,赋值运算符重载,都不需要手动写使用编译器提供的默认的即可

因为编译器给的默认的这四大成员函数,都有一个特性:
如果类的成员变量是其他类实例化的对象,调用本类的四大默认成员函数的时候,对其他类实例化的对象进行操作时就可以自动调用那个类自己的四大默认成员函数


empty

因为把queue的数据都存储在了适配器对象里面

所以判断适配器对象是否为空即可
加const是为了让const修饰的对象也能调用
bool empty()const 
{
	return _obj.empty();
}

size

因为把queue的数据都存储在了适配器对象里面

所以适配器对象的size,就是queue的size

加const是为了让const修饰的对象也能调用
size_t size()const
{
	return _obj.size();
}

front

因为把queue的数据都存储在了适配器对象里面

所以适配器对象中的第一个数据,就是队头的数据
T& front()
{
	return _obj.front();
}

const修饰的对象只能调用const修饰的成员函数
const T& front()const
{
	return _obj.front();
}

back

	因为把queue的数据都存储在了适配器对象里面

	所以适配器对象中的最后一个数据,就是队尾的数据
	T& back()
	{
		return _obj.back();
	}

	const修饰的对象只能调用const修饰的成员函数
	把返回值改成,const T&类型,防止修改
	const T& back()const
	{
		return _obj.back();
	}


push

因为  要把  queue的数据都存储在适配器对象里面

所以push就是尾插
void push(const T&val)
{
	_obj.push_back(val);
}

pop

因为把queue的数据都存储在了适配器对象里面

所以删除队头,就是适配器对象的头删
void pop()
{
	_obj.pop_front();
}


全部代码

#include<deque>
#include<list>
#include<iostream>

using namespace std;

namespace myqueue
{
	//T是queue里面存储的数据的类型
	//con是适配器的类型
	template<class T, class con = deque<T>>
	class queue
	{
	private:
		//使用适配器类 实例化的 适配器对象
		con _obj;


	public:
		//因为把queue的数据都存储在了适配器对象里面

		//所以判断适配器对象是否为空即可
		bool empty()const 
		{
			return _obj.empty();
		}

		//因为把queue的数据都存储在了适配器对象里面
		
		//所以适配器对象的size,就是queue的size
		size_t size()const
		{
			return _obj.size();
		}

		//因为把queue的数据都存储在了适配器对象里面
		
		//所以适配器对象中的第一个数据,就是队头的数据
		T& front()
		{
			return _obj.front();
		}

		//const修饰的对象只能调用const修饰的成员函数
		//把返回值改成,const T&类型,防止修改
		const T& front()const
		{
			return _obj.front();
		}


		//因为把queue的数据都存储在了适配器对象里面

		//所以适配器对象中的最后一个数据,就是队尾的数据
		T& back()
		{
			return _obj.back();
		}

		//const修饰的对象只能调用const修饰的成员函数
		//把返回值改成,const T&类型,防止修改
		const T& back()const
		{
			return _obj.back();
		}

		//因为  要把  queue的数据都存储在适配器对象里面

		//所以push就是尾插
		void push(const T&val)
		{
			_obj.push_back(val);
		}

		//因为把queue的数据都存储在了适配器对象里面

		//所以删除队头,就是适配器对象的头删
		void pop()
		{
			_obj.pop_front();
		}
	};
}


;