C++ 的容器适配器——从stack/queue看

STL 中的 stack 和 queue 并不是独立新建的容器类,而是"容器适配器":对底层容器接口的一层封装(包装),把底层容器暴露的接口变成特定的"栈/队列"接口。

默认情况下,STL 的 stack 和 queue 使用 deque 作为底层容器,因为 deque 在头尾操作和随机访问之间提供了折中性能。

什么是容器适配器

适配器的作用是"将一个类的接口转换为客户希望的另一个接口"。
stack:只允许在一端插入/删除;queue:允许在一端入、一端出(FIFO)。

适配器并不自己管理内存,而是复用底层容器(deque, vector, list 等)。

三种常见底层容器比较

  • vector
    • 优点:尾插尾删高效、连续内存、随机访问 O(1)。
    • 缺点:头插或中间插入删除代价高(O(n)),扩容涉及复制/移动。
  • list(双向链表)
    • 优点:任意位置插入/删除 O(1)(在已定位位置),不需要连续内存。
    • 缺点:不支持随机访问,内存开销大,缓存局部性差。
  • deque
    • 优点:头尾插入均高效、支持下标访问(不是完全连续但接近),适合做 stack/queue 默认容器。
    • 缺点:中间插入删除依然 O(n)。

复杂度速览(常见操作)

  • push_back / pop_back:vector O(1) 摇摆(扩容摊销)、deque O(1)、list O(1)
  • push_front / pop_front:deque O(1)、list O(1)、vector O(n)
  • 随机访问:vector O(1)、deque 接近 O(1)、list O(n)

实现 stack 适配器要点与常见错误

  • 模板默认参数:注意 template<class T, class Container = deque<T>>
  • 成员函数的 const 与 noexcept:比如 top()、size()、empty() 应该标记为 const(不改变对象状态),size() / empty() 可标记 noexcept。
  • top() 返回引用:应返回 const T& 或 T& 取决于用途,通常提供 const 版本和非常量版本。
  • 异常与未定义行为:对空栈调用 top() / pop() 是未定义行为(与 STL 一致);可以在包装层增加断言或抛出异常以增强调试信息。
  • 成员别名:提供 using container_type = Container; using value_type = T; 有助于与 STL 接口兼容。

改进后的 stack 参考实现

cpp 复制代码
namespace sqtque
{
    // 用 Container 适配转换出 stack
    template<class T, class Container = std::deque<T>>
    class stack
    {
    public:
        using value_type = T;
        using container_type = Container;
        using size_type = std::size_t;

        // 默认构造 / 析构由底层容器负责

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

        void push(T&& x)
        {
            _con.push_back(std::move(x));
        }

        void pop()
        {
            assert(!empty() && "pop on empty stack");
            _con.pop_back();
        }

        T& top()
        {
            assert(!empty() && "top on empty stack");
            return _con.back();
        }

        const T& top() const
        {
            assert(!empty() && "top on empty stack");
            return _con.back();
        }

        size_type size() const noexcept
        {
            return _con.size();
        }

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

    private:
        Container _con; // 封装出一个容器
    };
}

使用示例

cpp 复制代码
#include <iostream>
#include <vector>

int main()
{
    sqtque::stack<int> s; // 默认使用 deque<int>
    s.push(1);
    s.push(2);
    std::cout << s.top() << '\n'; // 2
    s.pop();
    std::cout << s.top() << '\n'; // 1

    // 使用 vector 作为底层容器(可行,但 vector 不支持 push_front)
    sqtque::stack<int, std::vector<int>> sv;
    sv.push(10);
    std::cout << sv.top() << '\n';
    return 0;
}

容易踩的坑

  • top() / pop() 在空容器上未定义行为,最好在调试或库接口中显式检查或断言。
  • 若要与标准 std::stack 完全兼容,考虑添加构造重载(接受容器参数)和访问底层容器的成员函数(如 c())。
  • 性能权衡:如果你只在尾部操作并且需要最高速的随机访问,用 vector;若需要稳定的头尾 O(1),用 deque;若频繁在中间插入删除且已持有迭代器,考虑 list。

结语

理解容器适配器的核心在于"接口的封装"和"底层容器的选择"。实现时应注意模板默认参数写法、成员函数的 const/ noexcept 语义与空容器的边界条件。通过上述改进可以让自实现的 stack 更安全、更 STL 风格、更易于扩展。

相关推荐
2601_962885725 小时前
如何用 Python计算 CR能量指标(中间意愿指标)?
开发语言·python
hetao17338375 小时前
校内场-提高组 ZYZSC-S-Round 2
c++·算法
hetao17338375 小时前
2026-10-14 hetao1733837 的刷题记录
c++·算法
临床数据科学和人工智能兴趣组5 小时前
在R语言的生态系统中,字符串处理一直是一个重要且复杂的任务
开发语言·正则表达式·r语言·r语言-4.2.1
蜗牛互联网5 小时前
Python调用Gemini Structured Outputs实现工单路由门禁
java·开发语言·人工智能·python
SEO_juper5 小时前
给网页做可引用性评分:判断内容够不够让 AI 引用(GEO 工具,附代码)
开发语言·人工智能·爬虫·python·seo·独立站
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 产品定位、功能模块与客户端原型解读
java·开发语言·交友
盛世宏博北京6 小时前
十二防档案库房总线架构设计:恒湿消毒净化一体机 RS485 组网实战笔记
开发语言·笔记·php·恒湿机·档案八防温湿度
浅念-6 小时前
动态规划:线性DP——多状态DP与子数组专题笔记
c++·笔记·算法·leetcode·职场和发展·动态规划·力扣
雪雪爱冲浪6 小时前
JavaScript 网络爬虫教程(2026)
开发语言·javascript·爬虫