当前位置: 首页 > news >正文

【C++】STL详解(八)—stack和queue的模拟实现

在这里插入图片描述

✨ 坚持用 清晰易懂的图解 + 代码语言, 让每个知识点都 简单直观
🚀 个人主页 :不呆头 · CSDN
🌱 代码仓库 :不呆头 · Gitee
📌 专栏系列

  • 📖 《C语言》
  • 🧩 《数据结构》
  • 💡 《C++》
  • 🐧 《Linux》

💬 座右铭“不患无位,患所以立。”


【C++】STL详解(八)—stack和queue的模拟实现

  • 摘要
  • 目录
    • 一、容器适配器
      • 1. 解释
      • 2. stack vs queue 对比表
    • 二、stack的模拟实现
      • 1. 常用函数
        • 1.1 类模板定义
        • 1.2 push
        • 1.3 pop
        • 1.4 top
        • 1.5 size
        • 1.6 empty
        • 1.7 swap
      • 2. .h(声明定义不分离)
      • 3. .c(测试)
    • 三、queue的模拟实现
      • 1. 常用函数
        • 1.1 push
        • 1.2 pop
        • 1.3 front
        • 1.4 back
        • 1.5 size
        • 1.6 empty
        • 1.7 swap
      • 2. .h(声明定义不分离)
      • 3. .c(测试)
    • 四、总结


stack与queue的详细介绍和使用请点击

摘要

不是呆头将在本文详细讲解了C++ STL 中 stack(栈)queue(队列) 的模拟实现:

  • 容器适配器概念:stack 与 queue 并非独立容器,而是基于已有顺序容器(deque / vector / list)的封装。
  • stack:后进先出(LIFO),只允许在栈顶操作元素;核心接口:push()pop()top()size()empty()swap()
  • queue:先进先出(FIFO),入队在队尾,出队在队头;核心接口:push()pop()front()back()size()empty()swap()
  • 模拟实现:通过底层容器操作完成栈/队列行为,同时支持自定义底层容器,保持高效与灵活性。

📌 编程箴言: “好的C++代码就像好酒,需要时间沉淀。”
(正文开始👇)


目录

一、容器适配器

1. 解释

在 C++ STL 中,stack 和 queue 并不是独立的数据结构容器,而是典型的 容器适配器(Container Adapter)。所谓“容器适配器”,就是它们本身不直接存储数据,而是基于已有的顺序容器(如 dequevectorlist)进行封装,并对外只提供一组受限的接口,从而实现特定的数据操作语义:

  • stack(栈) 👉 只允许在一端操作数据,遵循 后进先出(LIFO) 原则;底层默认使用 deque,也可指定 vectorlist 作为适配器容器。
  • queue(队列) 👉 只允许一端进、一端出,遵循 先进先出(FIFO) 原则;底层默认同样是 deque,也可以用 list 来替代。

因此,stack 和 queue 可以理解为:

在通用顺序容器的基础上,通过“适配器模式”屏蔽掉不必要的接口,仅保留特定操作(入/出、取顶/取首尾),从而保证语义清晰、逻辑简单、效率稳定。


2. stack vs queue 对比表

特性stack(栈)queue(队列)
数据结构类型容器适配器容器适配器
底层默认容器dequedeque
可选底层容器vector / listlist
数据操作规则后进先出(LIFO)先进先出(FIFO)
插入位置栈顶(top)队尾(back)
删除位置栈顶(top)队首(front)
主要接口push()pop()top()push()pop()front()back()
适用场景函数调用、表达式求值、括号匹配、DFS任务调度、消息队列、层序遍历、BFS

二、stack的模拟实现

1. 常用函数

1.1 类模板定义
template<typename T,typename Container = deque<T>>
class stack
{
public:private:Container _con;
};

template<typename T, typename Container = deque<T>>

类模板定义,T为储存的数据类型,Container为底层容器类型,默认是deque < T >

Container _con;

定义了一个名为stack的类,包装一个底层容器,然后基于这个底层容器来实现功能


1.2 push

对于stack,push是把数据压入,相当于数组的末尾插入,直接执行尾插操作,但是我们不知道函数的参数是内置类型还是自定义类型,所以我们可以使用引用传参来避免是自定义类型的大对象拷贝。

void push(const T& val)
{_con.push_back(val);
}

因为我们插入数据不需要进行修改,加上const修饰。


1.3 pop

对于stack,pop是把顶部的数据进行删除,从数组的角度看是删除数组最后一个数据,直接调用尾删就行。

void pop()
{_con.pop_back();
}

1.4 top

top是获取栈顶元素的引用,也就是获取数组中最后一个数据的引用,我们直接采用back,back的作用是返回数组最后一个数据的引用。

T& top()
{return _con.back();
}

1.5 size

size是获取栈中有效数据的个数,可以直接调用size();而且个数必须是非负数,我们可以直接采用size_t;获取有效个数并不会对容器数据做任何的修改,可以使用const修饰。

size_t size() const
{return _con.size();
}

1.6 empty

empty是通过bool来进行判断容器是否为空,我们可以直接调用empty()函数;同时这个操作不会对容器中的任何数据进行修改,所以可以加const进行修饰。

bool empty() const
{return _con.empty();
}

1.7 swap

swap是用来整体交换两个容器中的数据,为了避免把整个栈拷贝一份(非常耗时),所以我们直接采用引用传参;因为stack是容器适配器,底层容器默认是deque,为了更方便适配各种容器,我们需要一个存储数据类型的类和一个底层容器类;调换是调用底层容器自己的 swap 函数,直接交换两者的存储空间。
这样整个交换操作复杂度 O(1),只是指针/内部资源的交换,不会一一拷贝元素。

void swap(stack<T, Container>& st){_con.swap(st._con);}

2. .h(声明定义不分离)

#pragma once
#include<iostream>
#include<stack>
using namespace std;namespace dh //防止命名冲突
{ template<typename T, typename Container = deque<T>>//类模板定义,T为数据类型,Container为底层容器类型,默认是dequeclass 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();}//获取栈中有效元素个数size_t size() const{return _con.size();}//判断栈是否为空bool empty() const{return _con.empty();}//交换两个栈中的数据void swap(stack<T, Container>& st){_con.swap(st._con);}		private:Container _con;};
}

3. .c(测试)

#include"stack-queue.h"void test01()
{stack<int> st;stack<int> st1;st.push(5);st.push(2);st.push(0);st1.push(6);st1.push(6);st1.push(6);cout << st.size() << endl;cout << st1.size() << endl;while (!st.empty()){cout << st.top() << ' ';st.pop();}cout << endl;st.swap(st1);while (!st.empty()){cout << st.top() << ' ';st.pop();}cout << endl;cout << st.size() << endl;
}int main()
{test01();return 0;
}

在这里插入图片描述


三、queue的模拟实现

**注意:**类模板定义同上,T储存数据类型,Container为底层容器类型,默认是deque < T >

1. 常用函数

1.1 push

push是把数据从数据的末尾插入,直接执行尾插操作,但是我们不知道函数的参数是内置类型还是自定义类型,所以我们可以使用引用传参来避免是自定义类型的大对象拷贝。

void push(const T& val)
{_con.push_back(val);
}

因为我们插入数据不需要进行修改,加上const修饰。


1.2 pop

pop是把队头的数据进行删除,直接调用头删就行。

void pop()
{_con.pop_front();
}

1.3 front

front是用来获取队头数据的引用,我们可以直接调用front,它直接返回队头数据的引用

T& front()
{return _con.front();
}

1.4 back

back是用来获取队尾数据的引用,我们可以直接调用back,它直接返回队尾数据的引用

T& back()
{return _con.back();
}

1.5 size

size是获取容器中有效数据的个数,可以直接调用size();而且个数必须是非负数,我们可以直接采用size_t;获取有效个数并不会对容器数据做任何的修改,可以使用const修饰。

size_t size() const
{return _con.size();
}

1.6 empty

empty是通过bool来进行判断容器是否为空,我们可以直接调用empty()函数;同时这个操作不会对容器中的任何数据进行修改,所以可以加const进行修饰。

bool empty() const
{return _con.empty();
}

1.7 swap

swap是用来整体交换两个容器中的数据,为了避免把整个栈拷贝一份(非常耗时),所以我们直接采用引用传参;因为queue是容器适配器,底层容器默认是deque,为了更方便适配各种容器,我们需要一个存储数据类型的类和一个底层容器类;调换是调用底层容器自己的 swap 函数,直接交换两者的存储空间。
这样整个交换操作复杂度 O(1),只是指针/内部资源的交换,不会一一拷贝元素。

void swap(queue<T, Container>& q){_con.swap(q._con);}

2. .h(声明定义不分离)

#pragma once
#include<iostream>
#include<queue>
#include<deque>
using namespace std;namespace dh //防止命名冲突
{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();}//获取队列中有效元素个数size_t size() const{return _con.size();}//判断队列是否为空bool empty() const{return _con.empty();}//交换两个队列中的数据void swap(queue<T, Container>& q){_con.swap(q._con);}private:Container _con;};
}

3. .c(测试)

#include"stack-queue.h"void test02()
{queue<int> q;queue<int> q1;q.push(5);q.push(2);q.push(0);q1.push(6);q1.push(7);q1.push(8);cout << q.size() << endl;cout << q1.size() << endl;while (!q.empty()){cout << q.front() << ' ';q.pop();}cout << endl;q.swap(q1);while (!q.empty()){cout << q.front() << ' ';q.pop();}cout << endl;cout << q.size() << endl;
}int main()
{//test01();test02();return 0;
}

在这里插入图片描述


四、总结

  1. 容器适配器的本质:stack 和 queue 是对顺序容器的封装,屏蔽不必要的接口,只保留特定操作。

  2. 效率与安全:底层容器的 push_back / pop_back / push_front / pop_front 等操作保证了高效操作;swap 操作复杂度 O(1),无需逐元素拷贝。

  3. 使用场景

    • stack:函数调用管理、括号匹配、DFS 等。
    • queue:任务调度、消息队列、层序遍历、BFS 等。
  4. 实践建议:在自定义栈/队列时,可根据需要添加空栈/空队列保护,避免未定义行为。

🔹 总结一句话:
stack 是“后进先出”的守护者,queue 是“先进先出”的调度员,底层容器让它们高效而灵活。


不是呆头将一直坚持用清晰易懂的图解 + 代码语言,让每个知识点变得简单!
👁️ 【关注】 看一个非典型程序员如何用野路子解决正经问题
👍 【点赞】 给“不写八股文”的技术分享一点鼓励
🔖 【收藏】 把这些“奇怪但有用”的代码技巧打包带走
💬 【评论】 来聊聊——你遇到过最“呆头”的 Bug 是啥?
🗳️ 【投票】 您的投票是支持我前行的动力
技术没有标准答案,让我们一起用最有趣的方式,写出最靠谱的代码! 🎮💻

http://www.dtcms.com/a/389336.html

相关文章:

  • 【LeetCode Hot100----08-二叉树篇中(06-10),包含多种方法,详细思路与代码,让你一篇文章看懂所有!】
  • ARM(12) - ADC 检测光照强度
  • 网格生成引擎:设计原则、关键组件
  • 【开发AI】Spring AI Alibaba:集成AI应用的Java项目实战
  • Spark专题-第二部分:Spark SQL 入门(2)-算子介绍-Scan/Filter/Project
  • Selenium 自动化爬虫:处理动态电商页面
  • 无需Selenium:巧用Python捕获携程机票Ajax请求并解析JSON数据
  • Python版Kafka基础班 - 学习笔记
  • IDEA 查看 Maven 依赖树与解决 Jar 包冲突
  • 【LVS入门宝典】LVS与Nginx、HAProxy的对比:四层(LVS) vs 七层(Nginx)的适用场景
  • 系统安全配置与加固
  • 【AI-Agent】AI游戏库
  • 病毒库更新原理
  • 服务器内存爆炸,日志无报错,通过分析 Dump 文件查找问题原因
  • 【Redis学习】服务端高并发分布式结构演变之路
  • 【JavaScript 性能优化实战】第三篇:内存泄漏排查与根治方案
  • 关于JavaScript性能优化实战的技术
  • 分布式流处理与消息传递——Paxos Stream 算法详解
  • ​​瑞芯微RK3576多路AHD摄像头实测演示,触觉智能配套AHD硬件方案
  • mysql删除数据库命令,如何安全彻底地删除MySQL数据库?
  • vscode中创建项目、虚拟环境,安装项目并添加到工作空间完整步骤来了
  • 如何快速传输TB级数据?公司大数据传输的终极解决方案
  • Linux的进程调度及内核实现
  • 使用BeanUtils返回前端为空值?
  • Windows Server数据库服务器安全加固
  • Linux TCP/IP调优实战,性能提升200%
  • Amazon ElastiCache:提升应用性能的云端缓存解决方案
  • 查找并替换 Excel 中的数据:Java 指南
  • 多线服务器具体是指什么?
  • Golang语言基础篇001_常量变量与数据类型