本文代码已同步Github
一、stack的介绍与使用
1、介绍
首先还是先给出stack的接口文档:stack使用参考文档(stack - C++ Reference)
stack属于特殊的线性表,其特点是LIFO(后进先出)
对于stack,我们在前期的数据结构中已经学过其结构特点 ;
同时通过数组来模拟实现其底层结构
传送门:数据结构入门:栈实现全解析
2、使用
下面我们来看一下其接口

相较于vector,list,stack的接口少了很多;
我们同样整理出核心接口
| 函数说明 | 接口说明 |
|---|---|
stack() |
构造空的栈 |
empty() |
检测 stack 是否为空 |
size() |
返回 stack 中元素的个数 |
top() |
返回栈顶元素的引用 |
push() |
将元素 val 压入 stack 中 |
pop() |
将 stack 中尾部的元素弹出 |
都是一些很简单的接口,依次来使用

二、stack的模拟实现
注意⚠️:我们先根据 stack 的数据结构特点完成最直观的模拟实现
1、底层结构分析
由于stack后进先出特殊结构,我们该用什么样的方式来实现呢?
在前面学习数据结构时,我们采用顺序存储的结构来进行模拟实现;
为什么选择顺序存储呢?
答案就是栈本身就可以被看成一个特殊的一维数组:
push就是尾插,pop就是尾删,top就是末尾元素
那现在我们直接选择封装好的底层容器来进行模拟实现,省去了造轮子的功夫
对于push_back和pop_back,vector和list都有这两个接口,该怎么选呢?
stack 只需要在一端进行插入和删除,而 vector 的尾插、尾删都非常适合这一场景,因此可以使用 vector 作为底层容器
2、代码实现
① 基础框架
首先来完成基础框架的搭建,底层采用封装好的vector
cpp
//stack.h
#pragma once
#include <vector>
namespace stl
{
template<class T>
class stack
{
public:
private:
std::vector<T> _v;
};
}
下面来思考一下是否需要构造函数?
当成员变量是自定义类型时,构造函数会调用自定义类型的构造函数;
对于内置类型则是未定义的行为;'
显然,当前类不需要手动实现构造函数
② 核心接口
实现核心接口其实就是调用底层的vector的接口即可
cpp
public:
void push(const T& x)
{
_v.push_back(x);
}
void pop()
{
_v.pop_back();
}
T& top()
{
return _v.back();
}
const T& top() const
{
return _v.back();
}
const size_t size() const
{
return _v.size();
}
bool empty()
{
return _v.empty();
}
下面来测试一下

三、queue的介绍与使用
1、介绍
同样地,先给出queue的接口文档:queue的使用参考文档(queue - C++ Reference)
queue同样属于特殊的线性表,其特点是FIFO(先进先出)
对于queue,我们在学习数据结构时已经通过链式存储进行实现
同样给出传送门:数据结构队列详解:从概念到代码实现
2、使用
先来看接口

整理出核心接口
| 函数声明 | 接口说明 |
|---|---|
queue() |
构造空的队列 |
empty() |
检测队列是否为空,是返回 true,否则返回 false |
size() |
返回队列中有效元素的个数 |
front() |
返回队头元素的引用 |
back() |
返回队尾元素的引用 |
push() |
在队尾将元素 val 入队列 |
pop() |
将队头元素出队列 |
下面我们依次来测试使用

四、queue的模拟实现
注意⚠️:我们先根据 queue 的数据结构特点完成最直观的模拟实现
1、底层结构分析
由于queue时先进先出,需要多次头删,vector显然就不合适了;
那么便采用list作为底层容器来说实现queue
2、代码实现
① 基础框架
cpp
//queue.h
#pragma once
#include <list>
namespace stl
{
template<class T>
class queue
{
public:
private:
std::list<T> _lt;
};
}
由于成员变量时自定义类型,同样不需要手动实现构造函数;
编译器默认生成的构造函数对于自定义类型便会调用其构造函数;
② 核心接口
和stack的模拟实现同理,调用list的接口即可
cpp
public:
void push(const T& x)
{
_lt.push_back(x);
}
void pop()
{
_lt.pop_front();
}
T& front()
{
return _lt.front();
}
const T& front() const
{
return _lt.front();
}
T& back()
{
return _lt.back();
}
const T& back() const
{
return _lt.back();
}
const size_t size() const
{
return _lt.size();
}
bool empty()
{
return _lt.empty();
}
来测试一下

五、容器适配器
1、介绍适配器模式
容器适配器 是 C++ 标准库中的一种类模板 ,它本身不是完整的容器,而是对底层容器(如
vector、deque、list)的一层封装,目的是提供更精简、更专用的操作接口。
我们来看文档中对stack和queue的介绍


发现对stack和queue的描述是容器适配器,并非容器;
因此,我们可以通俗的理解:给通用容器套上一个"固定外壳",只保留特定场景需要的动作
同时能够发现:
stack和queue的底层采用的是deque作为容器
为什么呢?我们下一篇便会详细介绍;
2、优化代码
为了和文档中使用的容器保持一致,我们需要优化stack和queue的模拟实现
先对刚才实现的stack进行优化
其实就是加上deque作为类的模板参数
cpp
//stack.h
#pragma once
#include <deque>
namespace stl
{
template<class T,class Container = std::deque<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
T& top()
{
return _con.back();
}
const T& top() const
{
return _con.back();
}
const size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
接着来优化queue的实现
cpp
#pragma once
#include <list>
namespace stl
{
template<class T,class Container = std::deque<T>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
T& front()
{
return _con.front();
}
const T& front() const
{
return _con.front();
}
T& back()
{
return _con.back();
}
const T& back() const
{
return _con.back();
}
const size_t size() const
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
当没有显示传入用什么容器作为参数时,默认就会用缺省值deque来作为底层容器;
如果觉得有帮助,可以关注Github项目持续更新