在C++中,对于容器适配器的理解我们可以举个例子,比如现在给定一个容器vector,而我们希望它以栈的样子呈现出来,也就是可以支持后进先出,有栈的一些基本接口的操作等,此时我们不需要自己再重新写一个数据结构,而是把原来的容器vector封装一下,改变它的接口,就可以正常使用了,下面我用一段代码来实现栈去适配vector的操作:
#pragma once
#include <iostream>
#include <vector>
using namespace std;
template <class T, class Container = vector<T>>
class Stack
{
public:
void Push(const T& x)
{
_con.push_back(x);
}
void Pop()
{
_con.pop_back();
}
const T& Top()
{
return _con.back();
}
size_t Size() const
{
return _con.size();
}
bool Empty()
{
return _con.empty();
}
private:
Container _con;
};
//测试函数
void TestStack()
{
Stack<int, vector<int>> st;
//模板参数给了缺省值,template <class T, class Container = vector<T>>
Stack<int> st;
st.Push(1);
st.Push(2);
st.Push(3);
st.Push(4);
st.Push(5);
while (!st.Empty())
{
cout << st.Top()<<" ";
st.Pop();
}
cout << endl;
}
由以上代码可以看出,实现上是用栈来实现的,但是底层封装的容器是vector;当然也可以选用list作为它的底层容器,其原理都是一样的。
下面我再附上队列和优先级队列的关于适配器的实现代码:
//队列
#pragma once
#include <iostream>
#include <queue>
#include <list>
using namespace std;
template<class T,class Container>
class Queue
{
public:
void Push(const T& x)
{
_con.push_back(x);
}
void Pop()
{
_con.pop_front();
}
const T& back()
{
return _con.back();
}
const T& Front()
{
return _con.front();
}
bool Empty()
{
return _con.empty();
}
size_t Size()
{
return _con.size();
}
private:
Container _con;
};
void TestQueue()
{
Queue<int,list<int>> q;
q.Push(1);
q.Push(2);
q.Push(3);
q.Push(4);
q.Push(5);
while (!q.Empty())
{
cout << q.Front() << " ";
q.Pop();
}
cout << endl;
}
//优先级队列
此时需要注意的是,优先级队列的底层结构是堆,再插入和删除的时候需要调整。
#pragma once
#include <iostream>
using namespace std;
#include <vector>
#include <queue>
#include <functional>
template<class T,class Container=vector<T>,class Compare=less<T>>
class PriorityQueue
{
public:
//大堆
void Adjustup(int child)
{
int parents = (child - 1) / 2;
while (child>0)
{
Compare com;
//重载operator()
if (com(_con[parents] , _con[child]))
{
swap(_con[parents], _con[child]);
child = parents;
parents = (child - 1) / 2;
}
else
break;
}
}
void Adjustdown(size_t parents)
{
int child = parents * 2 + 1;
while (child < _con.size())
{
Compare com;
//找出左右孩子大的
if (child + 1 < _con.size() && com(_con[child] ,_con[child + 1]))
child++;
if (com(_con[parents] , _con[child]))
{
swap(_con[parents], _con[child]);
parents = child;
child = parents * 2 + 1;
}
else
break;
}
}
void Push(const T& x)
{
_con.push_back(x);
Adjustup(_con.size()-1);
}
void Pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
Adjustdown(0);
}
const T& Top()
{
return _con[0];
}
const T& Back()
{
return _con[_con.size() - 1];
}
bool Empty()
{
return _con.empty();
}
size_t Size()
{
return _con.size();
}
private:
Container _con;
};
//测试函数
void TestPriorityQueue()
{
PriorityQueue<int, vector<int>,greater<int>> q;
q.Push(1);
q.Push(2);
q.Push(3);
q.Push(4);
q.Push(5);
while (!q.Empty())
{
cout << q.Top() << " ";
q.Pop();
}
cout << endl;
}
然而在C++中,选择deque作为stack和queue的底层默认容器,原因主要有以下几点:
1、stack和queue不需要遍历(因此栈和队列都没有迭代器),只需要在固定的一端或者两端进行操作;
2、在stack元素增长时,deque比vector效率更高;queue中的元素增长时,deque不仅效率高,而且内存使用率高。
但是vector和list作为栈和队列的底层容器,有一些优缺点,例如vector可以支持随机访问,连续内存数组缓存利用率高;但是头部插入删除效率低,扩容成本高;list的优缺点和vector反之,这里我就不再一一陈述。
所以对于以上list和vector的优缺点问题,利用deque(双端队列)可以折中的解决。由于deque在现实问题中并不常用,我就对于它的实现不做介绍了,有需要的可以去看源代码。