目录
介绍:
一,queue结构的设计
二,priority_queue结构设计
三,stack结构设计
介绍:
适配器
适配器是一种设计模式,而设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计的总结,该模式是将一个类的接口转换成另一个类的接口。比如我们常用的交流适配器、电源插口适配器等,如下图:
容器模板
模板的使用可以帮助我们接收万能类型,平常我们最多用的也就是普通类型的使用,其实模板也可接收容器类型,并像函数缺省参数一样,进行缺省参数的使用,即函数参数传递的是对象,而模板传递的是类型,使用如下:
template <class T = int, class Container = vector<T>> //第一个匹配普通类型,默然类型为int,第二个为容器类型,默认类型为vector<T>
//......... 这里省略了具体的类模板或函数模板
在C++标准库中,有一些常用的容器适配器。容器适配器是将容器经过特定的设计,完成特定的功能,如queue队列结构、stack栈结构、priority_queue优先队列结构等。这里都需要传入特定的容器,将其接口经过特定的修改,使其具有独特的功能。
这里需注重强调一下,容器适配器不是容器,因此它没有迭代器,只有容器才有迭代器。因此,容器适配器不支持范围for等底层用迭代器实现的语法结构。
一,queue结构的设计
我们先来观察queue的底层实现结构,如下图:
这里,我们实现队列机制很简单,这里我们直接用Container容器接口功能即可,在编译阶段时对其进行实例化,具体逻辑解说请看下面:
//封装容器类型Container,可以使用容器内的所有接口功能,这里我们不用容器类型的管底层是如何实现的
template<class T = int, class Container = std::deque<T>>//模板缺省值的使用,跟函数缺省值一样
class queue
{
public:
//下面直接使用容器中的接口功能即可,唯一要注意的是容器类型中必须存在此接口
void push(const T x = T())
{
con.push_back(x);
}
void pop()
{
con.pop_front();
}
const T& front()
{
return con.front();
}
const T& back()
{
return con.back();
}
const size_t size()
{
return con.size();
}
bool empty()
{
return con.empty();
}
private:
Container con; //在编译阶段对其进行实例化,调用时会调用其的构造函数
};
二,priority_queue结构设计
priority_queue底层用堆数据结构实现,我们先观察以下类型接口:
上图中,第一个参数T表示数据类型;第二个参数表示容器类型,不说明时默认为vector<T>类型;第三个参数用来说明建大堆还是小堆,不说明时默认为less<T>类(默认 “ < ”,第一个参数小于第二个参数)建大堆,要想建小堆需要“ 大于 ”,即greater<T>类 (默认 “ > ”,第一个参数大于第二个参数)。
这里的第三个参数跟sort排序算法的第三个参数效果相同,都是用来控制功能效果。不同的是这里传递的是类less或greater的类类型,即greater<T>或less<T>,而sort的第三个参数是类less或greater的函数对象,即greater<T>()或less<T>()。
在设计这方面的结构前,我们先了解以下仿函数。仿函数其实就是在类中重载一个“ () ” 的函数,此函数通常也叫函数对象,即仿函数或函数对象是一个定义了含有 operator()
成员函数的类,且该函数可以像普通函数一样被调用。
这里需说明的是,从本质上来讲仿函数其实是一个类,而不是一个函数。它是一种行为类似函数的对象,可以像普通函数那样调用,有参数、有返回值。以下代码设计了一个仿函数:
#include <iostream>
using namespace std;
class A
{
public:bool operator()(int a, int b) {return a > b;}
private:int a;int b;
};
int main()
{A X;cout << X(5, 6) << endl; //直接使用类对象调用//等效于以下代码cout << X.operator()(5, 6) << endl;return 0;
}
实现此适配器跟实现堆结构一样,这里唯一要注意的是要运用第三个参数控制大堆结构和小堆结构,默认情况传递less建大堆。具体的代码细节和相关注意点如下:
template <class T>
class less
{
public:
bool operator()(const T& x, const T& y) {
return x < y;
}
};
template <class T>
class greater
{
public:
bool operator()(const T& x, const T& y) {
return x > y;
}
};
template <class T = int, class Container = std::vector<T>, class Compare = less<T> >
class priority_queue
{
public:
void Adjustup(Container& c) //向上调整算法建堆
{
int child = c.size() - 1;
int parent = (child - 1) / 2;//注意: 这里不能使用Container::iterator it;这里使用模板类的成员,必须指定具体类型才可使用,编译器识别不了具体指类型还是静态变量
//编译器这里是分开编译的,先编译模板,后编译实例化,然后两个地方合在一起
//当编译到模板这块还不知道Container是什么具体类型,无法使用迭代器,模板都存在这个问题
//这里可以使用template来说明是其类型,进行推演,等到实例化然后再去里面取
//不用template可用auto直接万能接收类型,等编译到实例化时进行确定
auto it = c.begin(); //这里不用函数和类,因此不能使用模板,直接auto识别即可
//例如: typedef std::vector<T>::iterator iterator; 错误,编译器识别不出,一样的原理
while (parent >= 0 && comp(*(it + parent), *(it + child))) {
std::swap(*(it + child), *(it + parent));
child = parent;
parent = (child - 1) / 2;
}
}
void Adjustdown(Container& c) { //向下调整算法建堆
int parent = 0;
int child = parent * 2 + 1;
auto it = c.begin();
while (child < c.size()) {
if (child + 1 < c.size() && comp(*(it + child), *(it + child + 1))) {
child++;
}
if (comp(*(it + parent), *(it + child))) {
std::swap(*(it + child), *(it + parent));
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
priority_queue()
{ }
template <class InputIterator>//与上同理,直接使用万能推演,编译到这块时,会根据后面进行推演
priority_queue(InputIterator first, InputIterator last)
:c(first, last)
{
for (int i = (c.size() - 1) / 2; i >= 0; i--) {
Adjustdown(c);
}
}
bool empty() const
{
return c.empty();
}
size_t size() const
{
return c.size();
}
const T& top() const
{
return c.front();
}
void push(const T& x)
{
c.push_back(x);
Adjustup(c);
}
void pop()
{
std::swap(c.front(), c.back());
c.pop_back();
Adjustdown(c);
}
private:
Container c;
Compare comp;
};
三,stack结构设计
这里的stack实现机制与queue结构逻辑一样,直接套用容器类型模板,运用其接口功能实现专门结构的功能即可。代码如下:
template<class T, class Container = std::deque<T>>
class stack
{
public:
void push(const T x = T())
{
con.push_back(x);
}
void pop()
{
con.pop_back();
}
const T& top()
{
return con.back();
}
const size_t size()
{
return con.size();
}
bool empty()
{
return con.empty();
}
private:
Container con;
};