【C++】容器适配器结构的设计

目录

介绍:

一,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;
};


四,容器与适配器的区别

容器

       容器是一种对象类型,它是独立实现的,主要用于存储和管理任意类型的数据,比如平常生活中的容器一般,针对的只是对数据进行简单的操作。

适配器

        适配器只是在容器基础上设计出的一种模式,它通过包装一个已有的容器来实现,改变其接口和行为,实现特定的数据结构或算法,使得底层的容器具有了新的功能。例如先进先出的queue、后进先出的stack等。

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值