目录

一、基本概念

二、主要特点

1、不是独立容器

2、限制操作接口

3、默认底层容器

三、C++中的三种容器适配器

四、底层容器选择

五、为什么需要容器适配器?

1、提供特定语义

2、简化接口

3、灵活性

六、容器适配器与普通容器的区别

七、实现原理简析

八、使用建议

九、通俗易懂解释容器适配器

1、用日常生活比喻

2、简单来说

3、举个具体例子

4、为什么要有这种设计?

5、一句话总结


        容器适配器是C++标准库中的一种特殊容器,它们基于现有的序列容器(如vector、deque、list等)提供特定的接口和行为,而不是独立的容器实现。

一、基本概念

容器适配器可以理解为:

  • 设计模式中的适配器:它"包装"了一个现有的容器类,提供一个新的接口

  • 受限视图:对底层容器提供特定操作方式的限制性视图

  • 接口转换器:将底层容器的接口转换为特定的操作方式

        适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。


二、主要特点

1、不是独立容器

  • 它们依赖于现有的序列容器(如vector、deque等)作为底层存储

  • 只是提供了不同的访问方式

2、限制操作接口

  • 只暴露特定操作,隐藏底层容器的其他功能

  • 例如stack只允许一端操作,queue只允许两端操作

3、默认底层容器

  • stack和queue默认使用deque作为底层容器

  • priority_queue默认使用vector作为底层容器


三、C++中的三种容器适配器

        虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为 容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:

  1. stack - 后进先出(LIFO)结构

  2. queue - 先进先出(FIFO)结构

  3. priority_queue - 优先级队列,元素按优先级顺序出队


四、底层容器选择

容器适配器可以使用不同的底层容器实现:

// stack可以使用vector、deque或list作为底层容器
stack<int, vector<int>> sv;  // 基于vector的stack
stack<int, list<int>> sl;    // 基于list的stack

// queue可以使用deque或list作为底层容器
queue<int, list<int>> ql;    // 基于list的queue

// priority_queue可以使用vector或deque作为底层容器
priority_queue<int, vector<int>> pq;

五、为什么需要容器适配器?

1、提供特定语义

  • 明确表达程序意图(如需要栈或队列行为)

  • 限制不合适的操作,防止误用

2、简化接口

  • 隐藏底层复杂实现,只暴露必要操作

  • 使代码更易读和维护

3、灵活性

  • 可以根据需要选择不同的底层容器

  • 例如需要快速随机访问可以选择vector,需要高效插入删除可以选择list


六、容器适配器与普通容器的区别

特性 普通容器 (vector, list等) 容器适配器 (stack, queue等)
独立性 独立实现 依赖底层容器
接口完整性 提供完整容器接口 只提供特定操作接口
迭代器支持 支持 不支持
元素访问方式 多种方式 受限访问方式
默认构造行为 空容器 基于默认底层容器构造

七、实现原理简析

容器适配器通常通过组合方式实现,例如stack的可能简化实现:

template <class T, class Container = deque<T>>
class stack {
protected:
    Container c;  // 底层容器
public:
    void push(const T& x) { c.push_back(x); }
    void pop() { c.pop_back(); }
    T& top() { return c.back(); }
    // 其他成员函数...
};

八、使用建议

  1. 默认情况:使用默认底层容器(通常是最优选择)

  2. 性能考虑

    • stack如果需要频繁扩容,vector可能更合适

    • queue如果频繁在两端操作,deque是更好选择

  3. 内存考虑:list作为底层容器不会有扩容开销,但内存使用不连续

  4. 安全考虑:容器适配器限制操作,减少误用可能性

容器适配器通过提供受限但更语义化的接口,使我们在特定场景下能写出更清晰、更安全的代码。


九、通俗易懂解释容器适配器

1、用日常生活比喻

可以把容器适配器想象成"多功能变形玩具"

  1. 基础玩具:就像乐高积木(相当于C++的vector、deque这些基础容器),你可以自由拼装任何形状

  2. 变形玩具:就像用乐高积木专门拼成的"手枪"或"汽车"(相当于stack、queue这些容器适配器):

    • 它们是用乐高拼成的

    • 但只能当"手枪"或"汽车"来玩,不能随便拆改

    • 玩法固定(手枪只能射击,汽车只能跑)

2、简单来说

容器适配器就是:

  • "戴着面具的容器":它里面其实装着vector/deque/list这些普通容器

  • "限制功能的容器":只让你用特定方式操作(比如栈只能后进先出)

  • "专用工具":像专门的开瓶器,只能开瓶盖不能干别的

3、举个具体例子

想象一个管道:

  1. 普通容器:像普通水管,两头都能进出水,还能从中间接水

  2. stack适配器:像灭火器,只能从顶部压入和弹出(后进先出)

    stack<int> s;  // 就像一个垂直的管子
    s.push(1);    // 从顶部放入1
    s.push(2);    // 再放入2
    cout << s.top(); // 只能看到顶部的2
    s.pop();       // 只能取出顶部的2
  3. queue适配器:像排队通道,后面进前面出(先进先出)

    queue<int> q;  // 像一个水平管道
    q.push(1);     // 从尾部放入1
    q.push(2);     // 放入2
    cout << q.front(); // 只能看到前面的1
    q.pop();       // 只能取出前面的1

4、为什么要有这种设计?

  1. 安全性:防止你乱用(就像灭火器设计成只能一种用法,防止误操作)

  2. 表达意图:一看就知道你要用栈或队列的逻辑

  3. 灵活性:可以自由选择底层用什么容器实现(就像灭火器可以用钢瓶或铝瓶)

5、一句话总结

        容器适配器就是"给普通容器套上特定操作规则的外壳",让它变成专用工具,既保留了底层容器的存储能力,又限制了操作方式。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐