news 2026/3/30 17:40:43

【C++STL】容器适配器——stack用法详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++STL】容器适配器——stack用法详解

1.栈的本质:容器适配器

首先理解关键点:C++中的stack不是独立的容器,而是“容器适配器”。它不自己管理内存,而是包装一个已有的底层容器(默认是deque),为其添加栈的接口(LIFO操作)。

2.为什么需要基于已有容器构造?

场景一:快速构建已有数据的栈视图

假设你有一个现成的数据集合,原本用vector或list存储,现在需要以“后进先出”的方式处理。这时你可以:

  • 选择一:写循环,逐个push进栈 → 需要额外代码,且效率较低

  • 选择二:直接用已有容器构造栈 → 简洁高效,底层直接复用已有数据

场景二:数据处理的阶段转换

程序中常有这种模式:

  1. 收集阶段:用vector等随机访问容器收集数据

  2. 处理阶段:需要按特定顺序(如逆序)处理
    这时将已有容器构造为栈,就是自然的转换桥梁。

场景三:复用已有容器特性

不同底层容器有不同特性:

  • deque(默认):两端增删快,内存非连续

  • vector:内存连续,缓存友好,但只能在末端增删

  • list:任何位置增删快,但内存不连续

当你的程序已经使用了特定容器,并想基于它实现栈操作时,这个构造函数让你能保持原有容器类型的同时获得栈接口

官网:cplusplus.com/reference/stack/stack/?kw=stack

一.构造函数

作为一个容器适配器,它只有一种构造函数

explicit stack(const container_type& ctnr = container_type());

这个构造函数有两种用法:

  1. 默认构造:创建空栈

  2. 使用已有容器构造:复制容器内容作为栈的初始内容

#include <iostream> #include <stack> #include <vector> #include <deque> int main() { // 1. 默认构造 - 空栈 std::stack<int> s1; // 使用默认的deque作为底层容器 std::cout << "s1 size: " << s1.size() << std::endl; // 输出: 0 // 2. 使用vector作为底层容器(指定容器类型) std::stack<int, std::vector<int>> s2; std::cout << "s2 size: " << s2.size() << std::endl; // 输出: 0 // 3. 使用已有容器初始化(复制容器内容) std::vector<int> vec = {1, 2, 3, 4, 5}; std::stack<int, std::vector<int>> s3(vec); // 使用vec初始化栈 std::cout << "s3 size: " << s3.size() << std::endl; // 输出: 5 // 查看栈顶元素(应该是vec的最后一个元素) std::cout << "s3 top: " << s3.top() << std::endl; // 输出: 5 // 4. 使用deque初始化(stack默认容器) std::deque<int> deq = {10, 20, 30}; std::stack<int> s4(deq); // 使用deque初始化栈 std::cout << "s4 top: " << s4.top() << std::endl; // 输出: 30 // 5. 弹出元素验证栈的顺序(LIFO) std::cout << "\nPopping s3 elements: "; while (!s3.empty()) { std::cout << s3.top() << " "; s3.pop(); } // 输出: 5 4 3 2 1 (后进先出) return 0; }

1. explicit 关键字的作用

// 错误示例:不能隐式转换 std::vector<int> vec = {1, 2, 3}; // std::stack<int> s = vec; // 错误!不能隐式转换 // 正确示例:必须显式调用构造函数 std::stack<int, std::vector<int>> s(vec); // 正确

2.底层容器类型

默认使用 deque<T>

可以指定其他容器,但必须支持:

  • back() - 访问最后一个元素
  • push_back() - 末尾添加元素
  • pop_back() - 删除末尾元素

我们再看一个例子

#include <iostream> #include <stack> #include <list> int main() { // 使用list作为底层容器初始化栈 std::list<std::string> tasks = {"Task A", "Task B", "Task C"}; std::stack<std::string, std::list<std::string>> taskStack(tasks); std::cout << "Processing tasks (LIFO):\n"; while (!taskStack.empty()) { std::cout << "Processing: " << taskStack.top() << std::endl; taskStack.pop(); } // 输出: // Processing: Task C // Processing: Task B // Processing: Task A return 0; }

二.常用操作

empty

  • 作用:检查栈是否为空
  • 返回:bool 类型,栈为空时返回 true,否则返回 false
#include <iostream> #include <stack> int main() { std::stack<int> myStack; // 检查空栈 if (myStack.empty()) { std::cout << "栈是空的" << std::endl; // 会执行这里 } else { std::cout << "栈不是空的" << std::endl; } // 添加元素后检查 myStack.push(10); if (!myStack.empty()) { std::cout << "现在栈不是空的" << std::endl; // 会执行这里 } // 清空栈后检查 myStack.pop(); if (myStack.empty()) { std::cout << "栈又变成空的了" << std::endl; // 会执行这里 } return 0; }

size

  • 作用:返回栈中当前元素的个数
  • 返回:无符号整型,表示元素数量
#include <iostream> #include <stack> int main() { std::stack<std::string> nameStack; // 初始大小 std::cout << "初始大小: " << nameStack.size() << std::endl; // 输出: 0 // 添加元素 nameStack.push("Alice"); nameStack.push("Bob"); nameStack.push("Charlie"); std::cout << "添加3个元素后的大小: " << nameStack.size() << std::endl; // 输出: 3 // 移除元素 nameStack.pop(); std::cout << "移除1个元素后的大小: " << nameStack.size() << std::endl; // 输出: 2 // 继续添加 nameStack.push("David"); nameStack.push("Eve"); std::cout << "再添加2个元素后的大小: " << nameStack.size() << std::endl; // 输出: 4 return 0; }

top

  • 作用:访问栈顶元素(下一个要处理的元素),但是不会移动栈顶元素
  • 返回栈顶元素的引用(可修改),但不删除该元素。
  • 注意:不会移除元素,只是查看。如果栈为空调用此函数是未定义行为
#include <iostream> #include <stack> int main() { std::stack<int> numbers; // 示例1: 基本使用 numbers.push(10); numbers.push(20); numbers.push(30); std::cout << "栈顶元素: " << numbers.top() << std::endl; // 输出: 30 // 示例2: 查看但不删除 std::cout << "再次查看栈顶: " << numbers.top() << std::endl; // 输出: 30 std::cout << "栈大小仍然是: " << numbers.size() << std::endl; // 输出: 3 // 示例3: 修改栈顶元素 numbers.top() = 99; // 修改栈顶元素的值 std::cout << "修改后栈顶元素: " << numbers.top() << std::endl; // 输出: 99 return 0; }

push

  • 作用:插入元素到栈顶,成为新的栈顶
  • 参数:要插入的元素值(会复制或移动)
#include <iostream> #include <stack> int main() { std::stack<int> numbers; // 基本使用 std::cout << "初始栈大小: " << numbers.size() << std::endl; numbers.push(10); std::cout << "push(10)后,栈顶: " << numbers.top() << std::endl; std::cout << "栈大小: " << numbers.size() << std::endl; numbers.push(20); std::cout << "push(20)后,栈顶: " << numbers.top() << std::endl; std::cout << "栈大小: " << numbers.size() << std::endl; numbers.push(30); std::cout << "push(30)后,栈顶: " << numbers.top() << std::endl; std::cout << "栈大小: " << numbers.size() << std::endl; return 0; }

emplace

  • 作用:在栈顶原地构造元素
  • 用法:相比 push 更高效,直接在栈顶位置构造对象,避免临时对象的创建和拷贝
  • 参数:构造元素所需的参数列表
#include <iostream> #include <stack> #include <string> class Person { public: std::string name; int age; // 构造函数 Person(std::string n, int a) : name(n), age(a) { std::cout << "Person对象被构造: " << name << std::endl; } // 拷贝构造函数 Person(const Person& other) : name(other.name), age(other.age) { std::cout << "Person对象被拷贝: " << name << std::endl; } // 移动构造函数 Person(Person&& other) noexcept : name(std::move(other.name)), age(other.age) { std::cout << "Person对象被移动: " << name << std::endl; } }; int main() { std::stack<Person> peopleStack; std::cout << "=== 使用 push() 创建临时对象 ===" << std::endl; // push会创建临时对象,然后拷贝或移动到栈中 peopleStack.push(Person("Alice", 25)); // 输出: // Person对象被构造: Alice (临时对象) // Person对象被移动: Alice (移动到栈中) std::cout << "\n=== 使用 emplace() 直接构造 ===" << std::endl; // emplace直接在栈中构造对象,避免临时对象 peopleStack.emplace("Bob", 30); // 输出: // Person对象被构造: Bob (直接在栈中构造) std::cout << "\n=== 访问栈顶 ===" << std::endl; std::cout << "栈顶人员: " << peopleStack.top().name << std::endl; // Bob // 更多emplace示例 std::stack<std::pair<int, std::string>> pairStack; // 使用push需要创建pair对象 pairStack.push(std::pair<int, std::string>(1, "Hello")); // 使用emplace更简洁高效 pairStack.emplace(2, "World"); // 直接在栈中构造pair std::cout << "\n栈顶pair: " << pairStack.top().first << ", " << pairStack.top().second << std::endl; return 0; }

pop

  • 作用:移除栈顶元素,弹出并丢弃当前栈顶元素
  • 注意:不返回被移除的元素,只移除。需要先使用 top() 获取值
#include <iostream> #include <stack> int main() { std::stack<int> numbers; // 添加元素 for (int i = 1; i <= 5; i++) { numbers.push(i * 10); std::cout << "push(" << i * 10 << ")" << std::endl; } std::cout << "\n当前栈大小: " << numbers.size() << std::endl; std::cout << "当前栈顶: " << numbers.top() << std::endl; std::cout << "\n=== 开始pop操作 ===" << std::endl; // 安全地pop元素 if (!numbers.empty()) { std::cout << "准备pop,当前栈顶: " << numbers.top() << std::endl; numbers.pop(); // 移除50 std::cout << "pop后栈顶变为: " << (numbers.empty() ? "空" : std::to_string(numbers.top())) << std::endl; } std::cout << "\n=== 循环pop所有元素 ===" << std::endl; int count = 0; while (!numbers.empty()) { count++; std::cout << "第" << count << "次pop前,栈顶: " << numbers.top() << std::endl; numbers.pop(); std::cout << "pop后栈大小: " << numbers.size() << std::endl; } std::cout << "\n最终栈是否为空: " << (numbers.empty() ? "是" : "否") << std::endl; return 0; }

swap

  • 作用:交换两个栈的内容
  • 用法:高效交换两个栈的所有元素(只交换内部指针,不逐个拷贝元素)
  • 参数:要交换的另一个栈
#include <iostream> #include <stack> #include <string> int main() { std::stack<std::string> stack1; std::stack<std::string> stack2; // 初始化stack1 stack1.push("Apple"); stack1.push("Banana"); stack1.push("Cherry"); // 初始化stack2 stack2.push("Dog"); stack2.push("Cat"); std::cout << "交换前:" << std::endl; std::cout << "stack1 大小: " << stack1.size() << ", 栈顶: " << (stack1.empty() ? "空" : stack1.top()) << std::endl; std::cout << "stack2 大小: " << stack2.size() << ", 栈顶: " << (stack2.empty() ? "空" : stack2.top()) << std::endl; // 交换两个栈的内容 stack1.swap(stack2); std::cout << "\n交换后:" << std::endl; std::cout << "stack1 大小: " << stack1.size() << ", 栈顶: " << (stack1.empty() ? "空" : stack1.top()) << std::endl; std::cout << "stack2 大小: " << stack2.size() << ", 栈顶: " << (stack2.empty() ? "空" : stack2.top()) << std::endl; return 0; }

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/25 14:37:14

必收藏!大模型四大行业应用实践详解(小白/程序员入门必备)

本文详细拆解大模型技术在汽车、金融、能源和电商四大核心行业的落地实践&#xff0c;结合具体应用场景补充实操逻辑&#xff0c;适合小白入门了解大模型行业价值&#xff0c;也方便程序员参考技术落地思路。在汽车领域&#xff0c;大模型实现智能座舱与自动驾驶的双重升级&…

作者头像 李华
网站建设 2026/3/15 8:56:36

质押挖矿+Swap交易所:DeFi新基建的“双核引擎”与万亿生态密码

引言&#xff1a;当代码成为金融世界的“新上帝”2025年&#xff0c;全球DeFi锁仓量突破1.2万亿美元&#xff0c;东南亚农民通过链上借贷协议获得实时农业贷款&#xff0c;非洲创业者用NFT票据完成跨境支付&#xff0c;华尔街基金经理开始用AI分析链上流动性池……这场始于2018…

作者头像 李华