std::stack的pop()不返回值,仅移除栈顶元素;top()返回栈顶引用但不检查空栈,必须先empty()再top()后pop(),否则导致未定义行为。

std::stack 的基本 push 与 pop 行为
std::stack 是适配器容器,默认底层用 std::deque 实现,不支持随机访问,只允许在栈顶进行 push() 和 pop()。调用 pop() 不返回值,仅移除栈顶元素;要获取值,必须先用 top(),再 pop() —— 这是新手最常混淆的点。
常见错误现象:stack.pop() 后直接用未检查的 stack.top(),导致未定义行为(如空栈访问);或误以为 pop() 返回元素而写成 int x = stack.pop();(编译失败)。
-
push(x):将x拷贝(或移动)到栈顶,时间复杂度均摊 O(1) -
top():返回栈顶元素的引用(T&或const T&),**不检查是否为空**,空栈调用是未定义行为 -
pop():移除栈顶元素,**不返回任何值**,空栈调用同样是未定义行为 - 安全使用前必须检查
empty(),不能依赖size() > 0做条件(虽等价,但语义不如empty()清晰)
正确配对 top() 和 pop() 的典型写法
因为 pop() 不返回值,实际取数需两步操作,且顺序不能颠倒。若中间有异常或提前 return,还可能引发资源泄漏(尤其含析构逻辑的对象),所以建议封装或加防护。
std::stacks; s.push(10); s.push(20); if (!s.empty()) { int val = s.top(); // 先读 s.pop(); // 再删 std::cout << val << "\n"; // 输出 20 }
注意:top() 返回的是引用,如果栈中存的是自定义类型且 top() 后栈被 pop() 或销毁,该引用立即失效——不能长期持有。
立即学习“C++免费学习笔记(深入)”;
不同类型参数对 push 的影响
push() 接收 const T& 或 T&&(C++11 起支持移动语义)。传入临时对象会触发移动(若类型支持),避免深拷贝:
- 传左值(如变量)→ 调用拷贝构造:
int x = 42; s.push(x); - 传右值(如字面量、
std::move(obj))→ 优先调用移动构造:s.push(std::string("hello")); - 自定义类需提供移动构造函数才能真正受益于移动语义
- 内置类型(
int、double)无移动/拷贝区别,性能一致
pop 空栈的后果与防御习惯
调用 pop() 或 top() 于空栈是未定义行为(UB),编译器不报错,运行时可能崩溃、静默出错或输出垃圾值。Release 模式下更难调试。
容易被忽略的地方:多线程环境下,即使检查了 empty(),也可能在判断后、top() 前被其他线程 pop() 掉——std::stack 本身**不是线程安全的**,所有操作都需外部加锁。
推荐防御写法(单线程):
if (!s.empty()) {
auto val = std::move(s.top()); // 移动语义适用时
s.pop();
use(val);
}
复杂点在于:没有原子性的 “pop-and-return” 成员函数。若频繁需要此语义,应自行封装一个安全函数,或改用 std::vector 手动维护栈顶索引(牺牲接口简洁性换控制力)。








