std::queue 是 C++ 标准模板库(STL)中一个重要的先进先出(FIFO)容器适配器。它默认基于 std::deque 实现,但也可以灵活指定其他满足特定接口要求的底层容器(如 std::list)。本文将系统性地讲解 std::queue 的核心特性、常用接口、完整示例以及在实际开发中的关键注意事项。
头文件与命名空间
使用 std::queue 必须包含头文件 <queue>,其所有接口都定义在 std 命名空间下。通常我们会配合其他头文件一起使用。
#include <queue> // 必须包含
#include <iostream>
#include <list> // 若需指定底层容器为list时包含
using namespace std; // 可选,也可显式使用 std::
核心特性
- FIFO规则:元素从队尾入队,从队头出队,保证了“先进先出”的顺序。
- 适配器特性:
std::queue 本身并不直接管理内存,而是通过封装一个底层容器(如 std::deque)来实现其功能。
- 访问受限:不支持随机访问(即无法通过下标
[] 或迭代器直接访问中间元素),只能访问队头和队尾。
常用接口详解
1. 构造函数
你可以通过多种方式构造一个队列:
queue<T> q;:默认构造一个空队列,底层使用 std::deque。
queue<T, Container> q;:指定底层容器类型(如 std::list)构造空队列。
queue<T> q(q2);:拷贝构造函数,复制另一个队列 q2 的所有元素。
// 默认构造(底层deque)
queue<int> q1;
// 指定底层容器为list
queue<int, list<int>> q2;
// 拷贝构造
queue<int> q3(q1);
2. 元素入队与出队
q.push(val):将元素 val 的拷贝添加到队尾。
q.emplace(args...):在队尾直接构造一个元素,参数 args... 会直接传递给元素的构造函数,通常比 push 更高效,避免了不必要的拷贝或移动操作。
q.pop():移除队头元素。注意:此函数不返回被移除的元素,调用前通常需要先用 front() 获取。
emplace 方法特别适用于构造复杂的对象,它接受的是构造对象所需的参数列表,而非对象本身。
queue<int> q;
q.push(10); // 队尾入队
q.push(20);
q.emplace(30); // 直接构造30并入队
q.pop(); // 移除队头10
// emplace 用于构造 pair
queue<pair<int, string>> q_pair;
q_pair.emplace(1, "hello"); // 使用两个参数构造一个 pair
// emplace 用于构造自定义结构体
struct Person {
int id;
string name;
Person(int i, string n) : id(i), name(n) {}
};
queue<Person> q_person;
q_person.emplace(101, "张三"); // 直接调用 Person 构造函数
3. 访问队头与队尾
q.front():返回队头元素的引用(可读可写)。
q.back():返回队尾元素的引用(可读可写)。
⚠️ 关键警告:在调用 front() 或 back() 之前,必须确保队列非空(使用 empty() 检查),否则会导致未定义行为(通常程序崩溃)。
queue<int> q;
q.push(10);
q.push(20);
cout << q.front() << endl; // 输出:10
cout << q.back() << endl; // 输出:20
q.front() = 100; // 修改队头元素的值
4. 容量与状态查询
q.empty():判断队列是否为空,为空返回 true。
q.size():返回队列中当前的元素数量,返回类型为 size_t。
q.swap(q2) 或 swap(q1, q2):交换两个队列的全部内容。
queue<int> q1, q2;
q1.push(1); q1.push(2);
q2.push(10); q2.push(20);
q1.swap(q2); // 交换后 q1 包含 [10, 20], q2 包含 [1, 2]
if (!q1.empty()) {
cout << “队列元素数:” << q1.size() << endl;
}
完整示例程序
以下代码综合演示了 std::queue 的主要操作:
#include <iostream>
#include <queue>
#include <list>
using namespace std;
int main() {
// 1. 构造队列(指定底层容器为list)
queue<int, list<int>> q;
// 2. 元素入队
q.push(10);
q.push(20);
q.emplace(30);
cout << “队列大小:” << q.size() << endl; // 输出:3
// 3. 访问队头队尾
cout << “队头:” << q.front() << endl; // 输出:10
cout << “队尾:” << q.back() << endl; // 输出:30
// 4. 遍历队列(通过出队方式)
cout << “队列元素:”;
while (!q.empty()) {
cout << q.front() << “ “; // 访问队头
q.pop(); // 移除队头
}
cout << endl; // 输出:10 20 30
// 5. 空队列判断
cout << “是否为空:” << boolalpha << q.empty() << endl; // 输出:true
return 0;
}
关键注意事项与最佳实践
pop() 不返回值:标准库设计如此,目的是保证异常安全。如果需要获取队头元素,必须遵循 front() -> pop() 的顺序。
- 严格检查空队列:在调用
front()、back() 或 pop() 之前,务必使用 empty() 进行判断,这是避免运行时错误的铁律。
- 底层容器要求:指定的底层容器必须提供
back()、front()、push_back()、pop_front()、empty() 和 size() 操作。std::deque(默认)和 std::list 都符合要求,而 std::vector 没有 pop_front() 方法,因此不能直接用作 queue 的底层容器。理解这些底层容器的特性,对于编写高性能的C++程序至关重要,更多关于系统编程和基础组件的内容可以参考网络/系统相关知识。
- 时间复杂度:所有核心操作(
push, pop, front, back, emplace)的时间复杂度均为 O(1)。
- 没有迭代器:
std::queue 故意不提供迭代器接口,以强化其作为纯 FIFO 队列的抽象。遍历的唯一方式就是连续执行“访问队头并出队”的操作。
典型应用场景
- 任务调度系统:如线程池中的任务等待队列,保证任务按提交顺序执行。
- 广度优先搜索(BFS):在图或树的遍历算法中,队列是实现 BFS 的核心数据结构,用于管理待访问的节点。掌握队列是深入理解算法/数据结构的关键一步。
- 消息缓冲:在生产者-消费者模型或消息传递系统中,用作缓冲区,确保消息的接收顺序与发送顺序一致。
- 打印队列:模拟现实生活中的排队场景,如网络数据包传输、事件处理等。
|