【C++】queue,stack和priority_queue 三种容器适配器

什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该中模式是将一个类的接口转换成客户希望的另外一个接口。
故适配器就是接口转换装置,让我们能通过特定的方法去操作原本自己不能直接操作的数据。
举个生活中的例子:电脑一般都用标准的交流的插头(USB接口),但我们平时用的欧洲壁式插座一般都是三孔的插座,这个时候我们就需要提供一个交流电适配器将插座的接口转换成USB的接口供我们使用。

什么是容器适配器

容器是对类模板的封装,而容器适配器是对容器的一种再封装。不同的容器适配器提供不同的函数,使容器的功能得到全新的特定的扩展。(但它和容器是有区别的:容器适配器不支持迭代器和算法,使用起来限制比较大)。标准库提供了三种容器适配器:queue(先进先出),stack(后进先出)和priority_queue(通过自己制定的规则进行排列,默认“<”比较即大数优先极高)。

为什么将stack,queue和priority_queue称为容器适配器

虽然以上三种也可以存放元素,但我们并没有将其划分在容器的行列,这是因为每个容器在底层都有自己的实现方式,可是stack,queue和priority_queue只是在底层将其他容器进行了一个封装,并没有自己的实现方式。如下图所示:
在这里插入图片描述

诺,,不信你看,vector底层就没有其他容器的支持,所以vector是容器,而priority_queue是容器适配器!
在这里插入图片描述

容器适配的模拟实现

适配器可以通过封装vector,list,deque三种容器实现。
(下面博客中有讲到:https://blog.csdn.net/ly_6699/article/details/88747183)

即如果我们希望用vector容器实现栈(后进先出)的相应接口,我们只需要封装vector,改变接口就可以正常使用啦。下面这个代码示范了
栈容器适配器stack的模拟实现

# pragma once
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;

template<class T,class Container=vector<T>>
//同样可以用template<class T,class Container=deque<T>>
//同样可以用template<class T,class Container=list<T>>
class Stack
{
public:
	Stack() {}
	void Push(const T&x)   { _c.push_back(x);}
	void Pop()             { _c.pop_back(); }
	T& Top()               { return _c.back(); }
	const T&Top() const    { const return  _c.back(); }
	size_t Size()const     { return _c.size(); }
	bool Empty()const      { return  _c.empty(); }
private:
	Container _c;
};

void Test1()
{
	//Stack<int,vector<int>> s;
	Stack <int>s;      //模板参数给了缺省值,可以直接定义
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);
	s.Push(5);
	cout <<"size: "<<s.Size() << endl;
	cout <<"top:  "<< s.Top() << endl;
	s.Pop();
	s.Pop();
	while (!s.Empty())
	{
		cout << s.Top() << " ";
		s.Pop();
	}
	cout << endl;
}
int main()
{
	Test1();
	system("pause");
	return 0;
}

队列容器适配器 queue的模拟实现
这里我用封装deque实现(先进先出)

# pragma once
#include <stdio.h>
#include <iostream>
using namespace std;
#include <deque>
template<class T,class Con=deque<T>>
class Queue
{
public:
	Queue()  {}
	void Push(const T&x)   { _c.push_back(x); }
	void Pop()             { _c.pop_front(); }
	T& Back()              { return _c.back(); }
	const T&Back() const   { const return  _c.back(); }
	T& Front()             { return _c.front(); }
	const T& Front()const  { const return _c.front(); }

	size_t Size()const     { return _c.size(); }
	bool Empty()const      { return  _c.empty(); }
private:
	Con _c;
};
void Test2()
{
	Queue<int> q;
	q.Push(1);
	q.Push(2);
	q.Push(3);
	q.Push(4);
	cout << "size: "<<q.Size() << endl;
	cout << "back: " << q.Back() << endl;
	cout << "front: " << q.Front() << endl;
	while (!q.Empty())
	{
		cout << q.Front() << " ";
		q.Pop();
	}
	cout << endl;
}

优先队列(priority_queue)的模拟实现在我的上篇博客里:https://blog.csdn.net/ly_6699/article/details/88653077 有兴趣可以去了解。

在C++中,虽然三种容器都可以封装成适配器,但我们选择deque作为stack和queue的底层默认容器,原因主要是:
1、stack和queue不需要遍历(因此栈和队列都没有迭代器),只需要在固定的一端或者两端进行操作;
2、在stack元素增长时,deque比vector效率更高;queue中的元素增长时,deque不仅效率高,而且内存使用率高。
vector和list作为栈和队列的底层容器,各有优缺点,例如vector可以支持随机访问,连续内存数组缓存利用率高;但是头部插入删除效率低,扩容成本高;list的优缺点和vector正好相反。
针对以上list和vector的优缺点问题,利用deque(双端队列)可以折中的解决。

容器适配器的使用

一般来说,先定义一个容器,接着给该容器配备相应的容器适配器,然后就可以使用该容器适配器特有的函数处理数据了。这里可以省略掉容器定义那一步,容器适配器将自动分配容器。下面这个代码示范了栈容器适配器stack的用法。我们先定义一个vector容器,接着给该vector容器分配stack容器适配器,然后使用栈容器提供的函数自顶向下的输出所有元素。

#include <iostream>
#include <vector>
// 使用容器适配器必须包含相应的头文件
#include <stack>
using namespace std;

int main()
{
	vector<int> v;
	// 将0-9存入vector容器并打印
	for (int i = 0; i<10; i++)
		v.push_back(i);
	cout << "输出Vector容器:" << endl;
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
		cout << *it << " ";
	cout << endl << endl;
	// 给容器v配上容器适配器
	stack< int, vector<int> > s(v);
	// 使用栈容器适配器stack提供的函数处理数据
	cout << "自顶向下输出栈( stack容器适配器 ):" << endl;
	while (!s.empty()) {
		cout << s.top() << " ";
		s.pop();
	}
	cout << endl;
	system("pause");
	return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ly_6699/article/details/88668289