C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ stack实战

C++ stack 全面解析与实战指南

作者:ljbguanli

这篇文章给大家介绍C++ stack全面解析与实战指南,本文将从stack的底层实现出发,详细讲解其核心特性、常用接口,结合实战案例演示具体用法,并梳理使用注意事项,帮助大家彻底掌握这一基础容器适配器,感兴趣的朋友跟随小编一起看看吧

在C++标准模板库(STL)中,stack(栈)是一种遵循“后进先出”(LIFO, Last In First Out)规则的容器适配器。它并非独立的容器,而是基于其他基础容器(如deque、vector、list)封装实现,屏蔽了部分基础容器的接口,仅暴露符合栈逻辑的操作。stack在日常开发中应用广泛,例如表达式求值、函数调用栈模拟、括号匹配等场景。本文将从stack的底层实现出发,详细讲解其核心特性、常用接口,结合实战案例演示具体用法,并梳理使用注意事项,帮助大家彻底掌握这一基础容器适配器。

一、stack 核心原理与特性

要理解stack的行为逻辑,首先需要明确其“容器适配器”的本质——它不直接管理内存,而是复用基础容器的内存管理和核心操作,仅对外提供统一的栈操作接口。

1.1 底层实现:基于基础容器的适配

stack的底层默认依赖deque容器实现(C++标准推荐,大部分编译器默认如此)。这是因为deque支持高效的尾部插入/删除操作(push_back、pop_back),且内存分配灵活,能很好地匹配栈的核心需求。同时,C++也支持指定其他符合要求的基础容器(需支持push_back、pop_back、back、empty、size这5个核心接口),例如vector、list等。

stack的适配逻辑非常简单:将栈的“压栈”对应基础容器的push_back(尾部插入),“出栈”对应基础容器的pop_back(尾部删除),“栈顶元素访问”对应基础容器的back(访问尾部元素)。通过这种适配,屏蔽了基础容器的头部操作、随机访问等接口,严格保证“后进先出”的规则。

1.2 核心特性总结

1.3 支持的基础容器

stack可指定的基础容器需满足“支持尾部插入/删除、访问尾部元素、判空、获取大小”这5个核心接口,STL中符合要求的容器有3个:

基础容器适配优势适用场景
deque(默认)尾部插入/删除效率高,内存分配灵活,无vector扩容时的大量数据拷贝开销大多数通用场景,推荐默认使用
vector内存连续,缓存命中率高,尾部操作效率稳定栈元素数量稳定,无需频繁扩容的场景
list尾部插入/删除效率高,无扩容开销,元素插入不会导致迭代器失效(但stack不支持迭代器)栈元素数量波动大,需频繁插入/删除的场景
指定基础容器的语法示例:
#include <stack>
  #include <vector>
    #include <list>
      // 基于vector的stack
      std::stack<int, std::vector<int>> stack_vec;
        // 基于list的stack
        std::stack<int, std::list<int>> stack_list;
          // 基于默认deque的stack(最常用)
          std::stack<int> stack_deque;
            }

二、C++ stack 常用接口详解

stack的接口设计简洁直观,仅包含与栈逻辑相关的核心操作。使用stack前,需包含头文件 <stack>,并使用std命名空间(或显式指定std::stack)。

2.1 构造与析构

接口原型功能说明示例
stack();默认构造函数,创建空stack(基础容器也为空)std::stack s;
explicit stack(const Container& cont);用已有的基础容器对象cont初始化stack,stack的元素与cont一致std::deque d{1,2,3}; std::stack s(d);
stack(const stack& other);拷贝构造函数,创建一个与other内容完全相同的stackstd::stack s1; s1.push(1); std::stack s2(s1);
~stack();析构函数,释放基础容器的资源-

2.2 核心操作(压栈、出栈、访问栈顶)

这是stack最常用的接口,直接对应栈的核心逻辑:

接口功能说明注意事项时间复杂度
push(const value_type& val)将val压入栈顶(调用基础容器的push_back)val会被拷贝/移动到容器中O(1)
emplace(Args&&… args)在栈顶直接构造元素(调用基础容器的emplace_back)避免拷贝,效率高于pushO(1)
pop()删除栈顶元素(调用基础容器的pop_back)不返回被删除的元素;栈为空时调用会导致未定义行为O(1)
top()返回栈顶元素的引用(调用基础容器的back)栈为空时调用会导致未定义行为;可通过top()修改栈顶元素(若元素非const)O(1)

2.3 容量相关

接口功能说明示例
empty()判断栈是否为空(调用基础容器的empty),空返回true,否则返回falseif (s.empty()) { … }
size()返回栈中元素的个数(调用基础容器的size)cout << “栈大小:” << s.size();

2.4 赋值操作

接口原型功能说明示例
stack& operator=(const stack& other);拷贝赋值,将other的内容赋值给当前stack,覆盖原有内容s1 = s2; // s1的内容变为s2的内容
stack& operator=(stack&& other) noexcept;移动赋值,将other的内容移动到当前stack,other变为空s1 = std::move(s2); // 高效转移资源

2.5 接口综合示例

#include <stack>
  #include <iostream>
    using namespace std;
    int main() {
    // 1. 构造空stack(默认deque为基础容器)
    stack<int> s;
      // 2. 压栈操作
      s.push(10);
      s.push(20);
      s.emplace(30); // 直接构造,效率更高
      cout << "栈大小:" << s.size() << endl; // 输出:3
      cout << "栈顶元素:" << s.top() << endl; // 输出:30(最后压入的元素)
      // 3. 修改栈顶元素(非const情况下)
      s.top() = 35;
      cout << "修改后栈顶元素:" << s.top() << endl; // 输出:35
      // 4. 出栈操作
      s.pop();
      cout << "出栈后栈顶元素:" << s.top() << endl; // 输出:20
      cout << "出栈后栈大小:" << s.size() << endl; // 输出:2
      // 5. 判空与清空(stack无clear接口,需通过pop循环清空)
      while (!s.empty()) {
      cout << "出栈元素:" << s.top() << endl;
      s.pop();
      }
      cout << "清空后栈是否为空:" << (s.empty() ? "是" : "否") << endl; // 输出:是
      // 6. 拷贝构造与赋值
      stack<int> s1;
        s1.push(1);
        s1.push(2);
        stack<int> s2(s1); // 拷贝构造
          cout << "s2栈顶元素:" << s2.top() << endl; // 输出:2
          stack<int> s3;
            s3 = s1; // 拷贝赋值
            cout << "s3栈大小:" << s3.size() << endl; // 输出:2
            return 0;
            }

输出结果:

栈大小:3
栈顶元素:30
修改后栈顶元素:35
出栈后栈顶元素:20
出栈后栈大小:2
出栈元素:20
出栈元素:10
清空后栈是否为空:是
s2栈顶元素:2
s3栈大小:2

三、stack 实战案例

stack的“后进先出”特性使其在多个经典场景中不可或缺,以下通过3个实战案例演示其实际应用:

3.1 场景1:括号匹配验证

需求:给定一个只包含括号(‘(’、‘)’、‘{’、‘}’、‘[’、‘]’)的字符串,判断字符串中的括号是否完全匹配(左右括号类型一致、顺序正确、无多余括号)。

思路:

#include <stack>
  #include <iostream>
    #include <vector>
      #include <string>
        using namespace std;
        int evalRPN(vector<string>& tokens) {
          stack<int> st;
            for (const string& token : tokens) {
            // 遇到运算符
            if (token == "+" || token == "-" || token == "*" || token == "/") {
            // 弹出两个操作数(注意顺序:先弹右操作数,后弹左操作数)
            int right = st.top();
            st.pop();
            int left = st.top();
            st.pop();
            // 计算结果并压栈
            if (token == "+") {
            st.push(left + right);
            } else if (token == "-") {
            st.push(left - right);
            } else if (token == "*") {
            st.push(left * right);
            } else if (token == "/") {
            // 除法向下取整(C++中负数除法需注意,此处按题目要求处理)
            st.push(left / right);
            }
            } else {
            // 遇到数字,转换为整数压栈
            st.push(stoi(token));
            }
            }
            // 栈中仅剩结果
            return st.top();
            }
            int main() {
            vector<string> tokens1 = {"2","1","+","3","*"}; // 等价于 (2+1)*3 = 9
              vector<string> tokens2 = {"4","13","5","/","+"}; // 等价于 4 + (13/5) = 6
                vector<string> tokens3 = {"10","6","9","3","+","-11","*","/","*","17","+","5","+"}; // 等价于 ((10*(6/(9+3*-11)))+17)+5 = 22
                  cout << "表达式1结果:" << evalRPN(tokens1) << endl; // 9
                  cout << "表达式2结果:" << evalRPN(tokens2) << endl; // 6
                  cout << "表达式3结果:" << evalRPN(tokens3) << endl; // 22
                  return 0;
                  }

3.2 场景2:逆波兰表达式求值

需求:逆波兰表达式(后缀表达式)是一种不含括号的表达式,运算符位于两个操作数之后,计算规则简单。给定一个逆波兰表达式的字符串数组,求其结果(假设表达式合法,仅包含数字和+、-、*、/四种运算符,除法向下取整)。

思路:

#include <stack>
  #include <iostream>
    #include <string>
      using namespace std;
      // 模拟函数调用(压栈)
      void callFunction(stack<string>& callStack, const string& funcName) {
        callStack.push(funcName);
        cout << "调用函数:" << funcName << endl;
        }
        // 模拟函数返回(出栈)
        void returnFunction(stack<string>& callStack) {
          if (callStack.empty()) {
          cout << "无正在执行的函数,无法返回!" << endl;
          return;
          }
          string funcName = callStack.top();
          callStack.pop();
          cout << "返回函数:" << funcName << endl;
          }
          int main() {
          stack<string> callStack;
            // 模拟函数调用流程
            callFunction(callStack, "main()");
            callFunction(callStack, "funcA()");
            callFunction(callStack, "funcB()");
            returnFunction(callStack); // funcB返回
            callFunction(callStack, "funcC()");
            returnFunction(callStack); // funcC返回
            returnFunction(callStack); // funcA返回
            returnFunction(callStack); // main返回
            returnFunction(callStack); // 无函数可返回
            return 0;
            }

3.3 场景3:模拟函数调用栈

需求:模拟程序的函数调用过程,记录函数的调用顺序和返回顺序(函数调用时压栈,函数返回时出栈)。

#include <stack>
  #include <iostream>
    #include <string>
      using namespace std;
      // 模拟函数调用(压栈)
      void callFunction(stack<string>& callStack, const string& funcName) {
        callStack.push(funcName);
        cout << "调用函数:" << funcName << endl;
        }
        // 模拟函数返回(出栈)
        void returnFunction(stack<string>& callStack) {
          if (callStack.empty()) {
          cout << "无正在执行的函数,无法返回!" << endl;
          return;
          }
          string funcName = callStack.top();
          callStack.pop();
          cout << "返回函数:" << funcName << endl;
          }
          int main() {
          stack<string> callStack;
            // 模拟函数调用流程
            callFunction(callStack, "main()");
            callFunction(callStack, "funcA()");
            callFunction(callStack, "funcB()");
            returnFunction(callStack); // funcB返回
            callFunction(callStack, "funcC()");
            returnFunction(callStack); // funcC返回
            returnFunction(callStack); // funcA返回
            returnFunction(callStack); // main返回
            returnFunction(callStack); // 无函数可返回
            return 0;
            }

输出结果:

调用函数:main()
调用函数:funcA()
调用函数:funcB()
返回函数:funcB()
调用函数:funcC()
返回函数:funcC()
返回函数:funcA()
返回函数:main()
无正在执行的函数,无法返回!

四、stack 使用注意事项

五、总结

stack是C++ STL中基于基础容器封装的“后进先出”容器适配器,核心优势是接口简洁、操作高效(压栈、出栈、访问栈顶均为O(1))。它不独立管理内存,而是复用deque、vector、list等基础容器的功能,严格屏蔽了非LIFO相关的接口,确保数据操作的规范性。

stack的经典应用场景包括括号匹配、表达式求值、函数调用栈模拟等,掌握其核心接口和使用注意事项,能帮助我们快速解决这类“后进先出”相关的问题。使用时需注意:调用pop()和top()前先判空、无迭代器无法遍历、清空需手动循环pop()等细节,避免出现未定义行为。

到此这篇关于C++ stack 全面解析与实战指南 - 指南的文章就介绍到这了,更多相关C++ stack实战内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文