Qt 队列容器 QQueue 的详解与示例
- [一、QQueue 的详解](#一、QQueue 的详解)
-
- 1、引言
- [2、 QQueue 的核心特性](#2、 QQueue 的核心特性)
- [3、 基本用法与接口详解](#3、 基本用法与接口详解)
-
- [3.1 、包含头文件与声明](#3.1 、包含头文件与声明)
- [3.2、 核心成员函数](#3.2、 核心成员函数)
- [4、 基础操作示例](#4、 基础操作示例)
-
- [4.1 、入列与出列](#4.1 、入列与出列)
- [4.2 、查看队头元素](#4.2 、查看队头元素)
- [5、 实际应用场景](#5、 实际应用场景)
-
- [5.1 、任务调度系统](#5.1 、任务调度系统)
- [5.2 、消息队列实现](#5.2 、消息队列实现)
- [5.3 、广度优先搜索(BFS)](#5.3 、广度优先搜索(BFS))
- 6、高级用法与技巧
-
- [6.1、 与 STL 算法结合](#6.1、 与 STL 算法结合)
- [6.2、 自定义对象队列](#6.2、 自定义对象队列)
- 7、性能考虑与最佳实践
-
- [7.1、 性能特点](#7.1、 性能特点)
- [7.2、 最佳实践](#7.2、 最佳实践)
- [8、 与 std::queue 的比较](#8、 与 std::queue 的比较)
- [9、 常见问题与解决方案](#9、 常见问题与解决方案)
-
- [Q1: `QQueue` 为空时调用 `dequeue()` 会怎样?](#Q1:
QQueue为空时调用dequeue()会怎样?) - [Q2: 如何清空队列但保留容量?](#Q2: 如何清空队列但保留容量?)
- [Q3: `QQueue` 是否线程安全?](#Q3:
QQueue是否线程安全?) - [Q4: 如何实现优先级队列?](#Q4: 如何实现优先级队列?)
- [Q1: `QQueue` 为空时调用 `dequeue()` 会怎样?](#Q1:
- 10、总结
- 二、代码示例

一、QQueue 的详解
1、引言
在 Qt 框架中,QQueue 是一个基于模板的容器类,它实现了先进先出(FIFO)的队列数据结构。作为 QList 的子类,QQueue 提供了专为队列操作优化的接口,使得在 Qt 应用中处理任务队列、消息传递、广度优先搜索等场景变得异常简单和高效。
本文将深入探讨 QQueue 的核心特性、常用接口,并通过丰富的 C++ 代码示例展示其在实际开发中的应用。
2、 QQueue 的核心特性
QQueue<T> 本质上是对 QList<T> 的封装,专门提供了队列的标准操作。其主要特性包括:
- 先进先出(FIFO):元素从队尾入列,从队头出列。
- 基于 QList :继承自
QList,因此拥有QList的所有优点,如快速的随机访问和隐式共享。 - 线程安全 :对于只读访问,多个线程可以同时访问一个
QQueue。但如果需要修改,则需要外部同步机制。 - 隐式共享:采用写时复制(Copy-on-Write)技术,在传递值时避免不必要的深拷贝,提升性能。
3、 基本用法与接口详解
3.1 、包含头文件与声明
要使用 QQueue,首先需要包含相应的头文件。
cpp
#include <QQueue>
#include <QDebug>
// 声明一个整型队列
QQueue<int> intQueue;
// 声明一个字符串队列
QQueue<QString> stringQueue;
3.2、 核心成员函数
QQueue 提供了一组简洁而强大的成员函数来操作队列:
| 函数 | 说明 | 时间复杂度 |
|---|---|---|
void enqueue(const T &value) |
将元素 value 添加到队尾 |
O(1) 平均 |
T dequeue() |
移除队头元素并返回它 | O(1) |
T &head() |
返回队头元素的引用(不移除) | O(1) |
const T &head() const |
返回队头元素的常量引用 | O(1) |
bool isEmpty() const |
判断队列是否为空 | O(1) |
int size() const |
返回队列中元素的数量 | O(1) |
void clear() |
清空队列中的所有元素 | O(n) |
bool contains(const T &value) const |
检查队列是否包含某个值 | O(n) |
int count(const T &value) const |
返回特定值在队列中出现的次数 | O(n) |
4、 基础操作示例
4.1 、入列与出列
这是 QQueue 最基本的使用场景。
cpp
#include <QQueue>
#include <QDebug>
int main() {
QQueue<QString> taskQueue;
// 入列操作
taskQueue.enqueue("处理用户登录");
taskQueue.enqueue("加载配置文件");
taskQueue.enqueue("初始化数据库连接");
qDebug() << "队列大小:" << taskQueue.size(); // 输出: 3
// 出列操作(先进先出)
while (!taskQueue.isEmpty()) {
QString task = taskQueue.dequeue();
qDebug() << "正在执行:" << task;
}
// 输出顺序:
// 正在执行: "处理用户登录"
// 正在执行: "加载配置文件"
// 正在执行: "初始化数据库连接"
return 0;
}
4.2 、查看队头元素
有时我们需要查看下一个要处理的元素,但不立即移除它。
cpp
QQueue<int> numberQueue;
numberQueue.enqueue(10);
numberQueue.enqueue(20);
numberQueue.enqueue(30);
// 查看队头元素
qDebug() << "队头元素:" << numberQueue.head(); // 输出: 10
// 队头元素仍然存在
qDebug() << "队列大小:" << numberQueue.size(); // 输出: 3
// 现在移除它
int first = numberQueue.dequeue();
qDebug() << "移除的元素:" << first; // 输出: 10
5、 实际应用场景
5.1 、任务调度系统
在需要顺序处理任务的系统中,QQueue 是理想的选择。
cpp
#include <QQueue>
#include <QThread>
#include <QDebug>
class Task {
public:
QString name;
int priority;
Task(const QString &n, int p) : name(n), priority(p) {}
};
class TaskScheduler {
private:
QQueue<Task> taskQueue;
public:
void addTask(const Task &task) {
taskQueue.enqueue(task);
qDebug() << "任务已添加:" << task.name;
}
void processTasks() {
while (!taskQueue.isEmpty()) {
Task currentTask = taskQueue.dequeue();
qDebug() << "正在处理任务:" << currentTask.name
<< "(优先级:" << currentTask.priority << ")";
// 模拟任务处理时间
QThread::msleep(100);
}
qDebug() << "所有任务处理完成!";
}
int pendingTasks() const {
return taskQueue.size();
}
};
// 使用示例
int main() {
TaskScheduler scheduler;
scheduler.addTask(Task("备份数据库", 1));
scheduler.addTask(Task("发送日报", 2));
scheduler.addTask(Task("清理临时文件", 3));
qDebug() << "等待处理的任务数:" << scheduler.pendingTasks();
scheduler.processTasks();
return 0;
}
5.2 、消息队列实现
QQueue 可以用于实现简单的消息队列,这在事件驱动架构中非常有用。
cpp
#include <QQueue>
#include <QString>
#include <QDebug>
class Message {
public:
QString type;
QVariant data;
qint64 timestamp;
Message(const QString &t, const QVariant &d)
: type(t), data(d), timestamp(QDateTime::currentMSecsSinceEpoch()) {}
};
class MessageQueue {
private:
QQueue<Message> messages;
QMutex mutex; // 用于线程安全
public:
void postMessage(const Message &msg) {
QMutexLocker locker(&mutex);
messages.enqueue(msg);
qDebug() << "消息已发布:" << msg.type << "at" << msg.timestamp;
}
Message getNextMessage() {
QMutexLocker locker(&mutex);
if (messages.isEmpty()) {
return Message("EMPTY", QVariant());
}
return messages.dequeue();
}
bool hasMessages() const {
QMutexLocker locker(&mutex);
return !messages.isEmpty();
}
};
5.3 、广度优先搜索(BFS)
在图论和树遍历算法中,QQueue 是实现 BFS 的天然选择。
cpp
#include <QQueue>
#include <QVector>
#include <QDebug>
class Graph {
private:
QVector<QVector<int>> adjacencyList;
public:
Graph(int vertices) : adjacencyList(vertices) {}
void addEdge(int from, int to) {
adjacencyList[from].append(to);
}
QVector<int> bfs(int startVertex) {
QVector<int> traversalOrder;
QVector<bool> visited(adjacencyList.size(), false);
QQueue<int> queue;
visited[startVertex] = true;
queue.enqueue(startVertex);
while (!queue.isEmpty()) {
int current = queue.dequeue();
traversalOrder.append(current);
for (int neighbor : adjacencyList[current]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.enqueue(neighbor);
}
}
}
return traversalOrder;
}
};
// 使用示例
int main() {
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 4);
g.addEdge(3, 5);
g.addEdge(4, 5);
QVector<int> bfsResult = g.bfs(0);
qDebug() << "BFS遍历顺序:" << bfsResult;
// 输出: 0, 1, 2, 3, 4, 5
return 0;
}
6、高级用法与技巧
6.1、 与 STL 算法结合
由于 QQueue 继承自 QList,它可以与 Qt 和 STL 算法无缝协作。
cpp
#include <QQueue>
#include <QDebug>
#include <algorithm>
int main() {
QQueue<int> queue;
queue.enqueue(5);
queue.enqueue(2);
queue.enqueue(8);
queue.enqueue(1);
queue.enqueue(9);
// 使用 std::find 查找元素
auto it = std::find(queue.begin(), queue.end(), 8);
if (it != queue.end()) {
qDebug() << "找到元素 8";
}
// 使用 Qt 算法计算总和
int sum = std::accumulate(queue.begin(), queue.end(), 0);
qDebug() << "队列元素总和:" << sum;
// 使用范围for循环遍历
qDebug() << "队列内容:";
for (int value : queue) {
qDebug() << value;
}
return 0;
}
6.2、 自定义对象队列
QQueue 可以存储任何可拷贝的类型,包括自定义类。
cpp
#include <QQueue>
#include <QDebug>
class Customer {
private:
QString name;
int customerId;
public:
Customer(const QString &n, int id) : name(n), customerId(id) {}
QString getName() const { return name; }
int getId() const { return customerId; }
// 重载输出操作符便于调试
friend QDebug operator<<(QDebug debug, const Customer &c) {
debug << "Customer[" << c.customerId << ":" << c.name << "]";
return debug;
}
};
int main() {
QQueue<Customer> customerQueue;
customerQueue.enqueue(Customer("张三", 1001));
customerQueue.enqueue(Customer("李四", 1002));
customerQueue.enqueue(Customer("王五", 1003));
// 处理客户队列
while (!customerQueue.isEmpty()) {
Customer nextCustomer = customerQueue.dequeue();
qDebug() << "正在服务:" << nextCustomer;
}
return 0;
}
7、性能考虑与最佳实践
7.1、 性能特点
- 入列/出列操作:平均 O(1) 时间复杂度
- 随机访问 :由于基于
QList,支持 O(1) 的随机访问 - 内存使用:使用连续内存存储,缓存友好
- 大规模数据:对于极大规模队列,考虑内存碎片问题
7.2、 最佳实践
-
预分配空间 :如果知道队列的大致大小,可以使用
reserve()预分配内存。cppQQueue<QString> largeQueue; largeQueue.reserve(10000); // 预分配空间 -
避免频繁拷贝:对于大型对象,考虑使用指针或共享指针。
cppQQueue<QSharedPointer<LargeObject>> objectQueue; -
线程安全:在多线程环境中使用队列时,务必添加适当的同步机制。
cppQQueue<Data> sharedQueue; QMutex queueMutex; // 线程1:生产者 { QMutexLocker locker(&queueMutex); sharedQueue.enqueue(newData); } // 线程2:消费者 { QMutexLocker locker(&queueMutex); if (!sharedQueue.isEmpty()) { Data data = sharedQueue.dequeue(); // 处理数据 } } -
异常安全 :
dequeue()在队列为空时会构造一个默认对象返回,这可能不是期望的行为。始终先检查isEmpty()。cppif (!queue.isEmpty()) { auto value = queue.dequeue(); // 安全使用 value }
8、 与 std::queue 的比较
| 特性 | QQueue<T> |
std::queue<T> |
|---|---|---|
| 底层容器 | QList<T> |
默认为 std::deque<T>,可指定 |
| 随机访问 | 支持(继承自 QList) | 不支持 |
| 隐式共享 | 支持 | 不支持 |
| Qt 集成 | 完美集成 Qt 类型系统 | 需要适配 |
| 序列化 | 支持 QDataStream |
需要自定义 |
| 调试输出 | 支持 QDebug 流输出 |
需要重载 operator<< |
选择建议:
- 在纯 Qt 项目中使用
QQueue,享受更好的集成和隐式共享带来的性能优势。 - 在跨平台或标准库优先的项目中考虑
std::queue。
9、 常见问题与解决方案
Q1: QQueue 为空时调用 dequeue() 会怎样?
A: 会构造一个默认的 T 对象并返回。这可能导致难以发现的逻辑错误。最佳实践是始终先检查 isEmpty()。
Q2: 如何清空队列但保留容量?
A: QQueue 没有直接的方法,但可以通过 QList 的接口实现:
cpp
queue.clear(); // 清空元素,可能释放内存
queue.reserve(capacity); // 重新保留容量
Q3: QQueue 是否线程安全?
A: 对于只读操作,多个线程可以同时访问。对于修改操作,需要外部同步(如使用 QMutex)。
Q4: 如何实现优先级队列?
A: QQueue 是严格的 FIFO 队列。如果需要优先级队列,可以使用 QPriorityQueue 或 std::priority_queue。
10、总结
QQueue 是 Qt 框架中一个简单而强大的队列容器,它:
- 提供了标准的 FIFO 队列接口
- 基于
QList,支持快速随机访问和隐式共享 - 与 Qt 生态系统完美集成
- 适用于任务调度、消息传递、BFS 等多种场景
通过合理使用 QQueue,您可以编写出更清晰、更高效的 Qt 应用程序。记住始终考虑线程安全和异常情况,特别是在生产环境中使用队列时。
二、代码示例
1、运行结果

2、示例代码
cpp
#include <QCoreApplication>
#include <QQueue>
#include <QDebug>
#include <QList>
// 自定义测试结构体(验证自定义数据存储)
struct MsgTask
{
int id;
QString info;
MsgTask(int i, const QString& s) : id(i), info(s) {}
// 重载打印
friend QDebug operator<<(QDebug dbg, const MsgTask& t)
{
dbg << "Task{id:" << t.id << ", info:" << t.info << "}";
return dbg;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "========= 1. 基础入队 enqueue() ==========";
QQueue<QString> strQueue;
strQueue.enqueue("A");
strQueue.enqueue("B");
strQueue.enqueue("C");
qDebug() << "队列全部元素:" << strQueue;
qDebug() << "队列长度 size():" << strQueue.size();
qDebug() << "是否为空 isEmpty():" << strQueue.isEmpty();
qDebug() << "\n========= 2. 获取队头/队尾 front() / back() =========";
qDebug() << "队首 front():" << strQueue.front();
qDebug() << "队尾 back():" << strQueue.back();
qDebug() << "\n========= 3. 出队 dequeue()(先进先出FIFO) =========";
QString out1 = strQueue.dequeue();
qDebug() << "弹出元素:" << out1 << " 剩余队列:" << strQueue;
qDebug() << "\n========= 4. 下标访问 at() =========";
qDebug() << "at(0):" << strQueue.at(0);
qDebug() << "\n========= 5. 遍历方式:范围for / STL迭代器 =========";
qDebug() << "范围for 遍历:";
for (const auto& item : strQueue)
qDebug() << " " << item;
qDebug() << "迭代器遍历:";
for (auto it = strQueue.begin(); it != strQueue.end(); ++it)
qDebug() << " " << *it;
qDebug() << "\n========= 6. 拷贝构造 / 赋值运算符 =========";
QQueue<QString> copyQueue = strQueue;
copyQueue.enqueue("D");
qDebug() << "原队列:" << strQueue;
qDebug() << "拷贝队列:" << copyQueue;
qDebug() << "\n========= 7. 清空队列 clear() =========";
copyQueue.clear();
qDebug() << "清空后长度:" << copyQueue.size();
qDebug() << "\n========= 8. 存储自定义结构体 =========";
QQueue<MsgTask> taskQueue;
taskQueue.enqueue(MsgTask(1, "读取传感器"));
taskQueue.enqueue(MsgTask(2, "上报数据"));
qDebug() << "自定义结构体队列:" << taskQueue;
qDebug() << "弹出任务:" << taskQueue.dequeue();
qDebug() << "\n========= 9. Qt6 无 push/pop,统一 enqueue/dequeue =========";
QQueue<int> stlQueue;
stlQueue.enqueue(100);
stlQueue.enqueue(200);
qDebug() << "队首 front:" << stlQueue.front();
stlQueue.dequeue();
qDebug() << "dequeue后队列:" << stlQueue;
qDebug() << "\n========= 10. 容器互转 QQueue <-> QList =========";
QQueue<double> numQueue;
numQueue.enqueue(1.1);
numQueue.enqueue(2.2);
// QQueue 转 QList
QList<double> list = numQueue.toList();
qDebug() << "转QList:" << list;
// 低版本Qt6无fromList,手动遍历填充
QQueue<double> fromListQueue;
for(double val : list)
{
fromListQueue.enqueue(val);
}
qDebug() << "从List构建队列:" << fromListQueue;
qDebug() << "\n========= 11. 原位构造替代 emplace(Qt6无emplace) =========";
QQueue<QString> moveQueue;
moveQueue.enqueue(QString("原位构造字符串"));
qDebug() << "入队元素:" << moveQueue.front();
qDebug() << "\n========= 12. 过滤、查找 contains() =========";
QQueue<QString> filterQueue;
filterQueue.enqueue("apple");
filterQueue.enqueue("banana");
filterQueue.enqueue("orange");
filterQueue.enqueue("apple");
qDebug() << "是否包含 banana:" << filterQueue.contains("banana");
qDebug() << "是否包含 grape:" << filterQueue.contains("grape");
qDebug() << "\n========= 13. 交换队列 swap() =========";
QQueue<int> q1;
q1.enqueue(1); q1.enqueue(2); q1.enqueue(3);
QQueue<int> q2;
q2.enqueue(10); q2.enqueue(20);
q1.swap(q2);
qDebug() << "交换后 q1:" << q1 << " q2:" << q2;
qDebug() << "\n========= 14. 边界异常演示(空队列禁止front/dequeue) =========";
QQueue<int> emptyQ;
if (!emptyQ.isEmpty())
{
emptyQ.front();
emptyQ.dequeue();
}
else
{
qDebug() << "空队列禁止调用 front/dequeue,必须先判空";
}
qDebug() << "\n========= 15. 常量队列 const QQueue =========";
QQueue<QString> tempConstQueue;
tempConstQueue.enqueue("X");
tempConstQueue.enqueue("Y");
const QQueue<QString> constQ = tempConstQueue;
qDebug() << "const队列 front:" << constQ.front();
// constQ.enqueue("Z"); // 编译报错,常量队列不可修改
return a.exec();
}
