c++中如何使用stack栈_c++ stack进栈出栈基本操作【详解】

std::stack需逐个push或用底层容器构造,不支持初始化列表;pop后调用top为未定义行为;无迭代器,不可遍历;自定义类型须满足可移动/复制且析构函数应noexcept。

如何声明和初始化一个 std::stack

标准库中的 std::stack 是容器适配器,不是独立容器,它默认底层用 std::deque 实现。不能直接用 std::stack s = {1, 2, 3}; 初始化(不支持初始化列表构造)。

正确方式是逐个 push,或用其他容器构造后赋值:

std::stack s;
s.push(1);
s.push(2);
s.push(3);

也可以指定底层容器类型,比如改用 std::vector

std::stack

, std::vector> s_vec;

注意:改底层容器会影响性能特征——deque 支持两端高效操作,vector 尾部 push/pop 快但头部慢(不过 stack 只用尾部,所以实际差别不大)。

push()pop()top() 的使用陷阱

push() 插入元素到栈顶;pop() 删除栈顶元素但不返回值;top() 返回栈顶引用,但不移除元素。三者必须配合使用,否则容易出错。

  • pop() 后再调用 top() 是未定义行为(UB),因为栈已空
  • top() 返回的是引用,若栈中存的是临时对象或即将析构的对象,取引用可能悬空
  • 没有 isEmpty() 成员函数,要用 s.empty() 判断是否为空

安全读取栈顶并弹出的惯用写法:

if (!s.empty()) {
    int val = s.top();
    s.pop();
    // use val
}

遍历 std::stack 的现实限制

std::stack 不提供迭代器接口,也不支持随机访问。它严格遵循 LIFO 契约,设计上就**不允许遍历**。

如果你需要查看所有元素(比如调试、日志或算法验证),有几种应对方式:

  • std::vectorstd::deque 手动模拟栈行为(牺牲接口简洁性,换取可访问性)
  • 把栈内容导出到另一个容器再处理(需反复 pop/push 恢复,O(n) 时间且破坏原栈)
  • 改用 std::deque 并只用 push_back()/pop_back() —— 它既支持栈操作,又支持遍历和随机访问

别试图通过友元或 reinterpret_cast 强行访问内部容器,标准未规定其成员名,跨编译器不可靠。

自定义类型入栈要注意什么

只要类型满足“可复制/可移动”,就能进 std::stack。但常见坑点包括:

  • 类中含指针成员,未定义深拷贝构造函数 → push 后多个栈元素共享同一块内存
  • 移动语义未正确实现,导致 push 大对象时频繁拷贝(C++11 后优先触发移动)
  • 类的析构函数抛异常 → pop() 时可能终止程序(栈操作期间异常传播极难处理)

建议对自定义类型启用 = default 移动操作,并确保析构函数为 noexcept

struct Node {
    int data;
    Node(int d) : data(d) {}
    Node(Node&&) noexcept = default;
    Node& operator=(Node&&) noexcept = default;
    ~Node() noexcept = default; // 关键:noexcept
};

栈本身不管理资源生命周期,它只负责存储对象副本或移动后的对象。资源泄漏风险完全由你定义的类型承担。