首页 > 编程笔记

C++ queue容器适配器用法详解

在 C++ 中,容器适配器是一种特殊的容器类,它实际上不是自己存储数据,而是依赖于其它的容器,例如 vector、list、deque 等。

简单理解,容器适配器就是对已有容器进行了一次封装,对容器的使用做了更严格的限定,或者进行了扩展。

本节要讲的 queue 就是 C++ 标准库提供的一个容器适配器。

默认情况下,queue 底层使用的是 deque 容器,deque 容器允许从两端插入、删除元素,且提供了迭代器可以遍历元素,而 queue 对这些功能做了限制,它只允许从一端插入元素,从另一端删除元素,且没有提供迭代器,不支持直接遍历元素。

queue容器适配器的创建

queue 本质是一个模板类,定义在<queue>头文件中。

默认情况下,queue 底层使用的是 deque 容器:
template <class T, class Container = deque<T> > class queue;
可以看到 template 的第二个参数 Container 默认指定的是 deque。除了 deque 之外,queue 的底层还可以使用 list 容器。

构造 queue 容器的方式有多种,包括:

1) 创建一个空的 queue 容器适配器,其底层使用默认的 deque 容器:
std::queue<int> values;
通过此行代码,就可以成功创建一个可存储 int 类型元素,底层采用 deque 容器的 queue 容器适配器。

2) 当然,也可以手动指定 queue 容器适配器底层采用 list 容器
std::queue<int, std::list<int>> values;
注意,在手动指定基础容器的类型时,其存储的数据类型必须和 queue 容器适配器存储的元素类型保持一致。

3) 可以用基础容器来初始化 queue 容器适配器,只要该容器类型和 queue 底层使用的基础容器类型相同即可。例如:
std::deque<int> values{1,2,3};
std::queue<int> my_queue(values);
由于 my_queue 底层采用的是 deque 容器,和 values 类型一致,且存储的也都是 int 类型元素,因此可以用 values 对 my_queue 进行初始化。

4) 还可以直接通过 queue 容器适配器来初始化另一个 queue 容器适配器,只要它们存储的元素类型以及底层采用的基础容器类型相同即可。例如:
std::deque<int> values{1,2,3};
std::queue<int> my_queue1(values);
std::queue<int> my_queue(my_queue1); //等价于 std::queue<int> my_queue = my_queue1;
注意,用 queue 适配器给另一个 queue 进行初始化有 2 种方式,都可以。

第 3、4 种初始化方法中,my_queue 容器适配器的数据是经过拷贝得来的,也就是说操作 my_queue 容器适配器中的数据,并不会对 values 容器以及 my_queue1 容器适配器有任何影响。

queue容器适配器的使用

queue 容器不提供迭代器,不支持遍历容器中的元素。此外,queue 容器中元素的进出必须遵循“先进先出”的原则,从一端进,从另一端出。

下表列出了操作 queue 容器的常用成员函数。

表 2 queue容器适配器支持的成员函数
成员函数 功能
empty() 如果 queue 中没有元素的话,返回 true。
size() 返回 queue 中元素的个数。
front() 返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
back() 返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
push(const T& obj) 在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
emplace() 在 queue 的尾部直接添加一个元素。
push(T&& obj) 以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
pop() 删除 queue 中的第一个元素。
swap(queue<T> &other_queue) 将两个 queue 容器适配器中的元素进行互换,需要注意的是,进行互换的 2 个 queue 容器适配器中存储的元素类型以及底层采用的基础容器类型,都必须相同。

下面的 C++ 程序演示了表中部分成员函数的用法:
#include <iostream>
#include <queue>

int main() {
    std::queue<int> q;

    // 入队
    q.push(1);
    q.push(2);
    q.push(3);

    // 出队
    q.pop();

    // 访问队首元素
    std::cout << "Front element: " << q.front() << std::endl;

    // 访问队尾元素
    std::cout << "Back element: " << q.back() << std::endl;

    // 检查队列是否为空
    if(!q.empty()) {
        std::cout << "The queue is not empty.\n";
    }

    // 队列大小
    std::cout << "Size: " << q.size() << std::endl;

    return 0;
}
运行结果为:

Front element: 2
Back element: 3
The queue is not empty.
Size: 2

总结

容器适配器提供了一种非常有效的方式来限制或扩展底层容器的功能。

和 list 和 deque 容器相比,queue 容器对他们的用法做了限制,主要包括:

推荐阅读