全部的实现代码放在了文章末尾
queue的模拟实现和stack一样,采用了C++适配器模式
queue
的适配器一般是deque
,也可以是list
因为queue是有特殊限制的线性表【只能在队头删除,队尾插入】,所以只要是线性结构并且可以高效的实现头插和尾删的线性表,就都可以作为queue的适配器
什么是适配器模式?
适配器模式是一种设计模式,它允许将不兼容接口的类一起工作。
适配器模式通常用于以下情况:
- 希望使用一个类,但其接口与其他代码不兼容。
- 希望创建一个可重用的类,它能够将接口转换为其他接口。
- 希望使用第三方库或遗留代码,但其接口与其他代码不兼容。
适配器模式通常包括以下三个主要部分:
- 目标接口(Target):这是期望使用的接口,客户端代码只能与目标接口交互。
- 源接口(Adaptee):这是需要适配的类,其接口与目标接口不兼容。
- 适配器(Adapter):这是一个类,它实现了目标接口,并将调用转换为对源接口的调用。适配器将源接口的调用转换为目标接口的调用,使得客户端代码可以与目标接口交互。
可以类比我们生活中的家庭电源接口
和笔记本电脑充电口
与电源适配器
,它们之间也是一种适配器关系
笔记本电脑充电口
是上面提到的目标接口
家庭电源接口
是上面提到的源接口
电源适配器
是上面提到的适配器
笔记本电脑的充电口
是不能和家庭电源接口
直接连接进行充电的,因为笔记本电脑用的是直流电,而家庭电源输出的是交流电,所以要把交流电转换为直流电才能给笔记本电脑供电,而电源适配器
就能做到这一点
对应了上面提到的适配器模式解决的问题:
可以将不兼容接口的类一起工作
准备工作
创建两个文件,一个头文件myqueue.hpp
,一个源文件test.cpp
【因为模板的声明和定义不能
分处于不同的文件中,所以把成员函数的声明和定义放在了同一个文件myqueue.hpp
中】
myqueue.hpp:存放包含的头文件,命名空间的定义,成员函数和命名空间中的函数的定义
test.cpp:存放main函数,以及测试代码
包含头文件
iostream:用于输入输出
list:提供list类型的适配对象
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();
}
};
}