队列(Queue)作为计算机科学中最基础的数据结构之一,其核心特性可以类比现实生活中的排队场景。想象一下超市收银台前的队伍——新来的顾客总是排在队尾(enqueue操作),而接受服务的顾客总是从队头离开(dequeue操作),这种先进先出(FIFO: First In First Out)的特性正是队列的本质。
在C++标准模板库(STL)中,queue容器适配器完美封装了这种数据结构。与需要手动实现指针操作的传统队列相比,STL queue提供了开箱即用的高效实现。其底层默认基于deque(双端队列)实现,这意味着在大多数情况下,元素的插入和删除操作都能达到O(1)时间复杂度。
关键特性备忘:STL queue不支持随机访问,也不提供迭代器接口,这是为了严格遵循队列的访问约束。如果需要更灵活的操作,可以考虑deque或list。
cpp复制#include <queue>
using namespace std;
queue<int> q; // 声明整型队列
q.push(10); // 队尾插入元素
q.front(); // 获取队首元素(不删除)
q.back(); // 获取队尾元素(不删除)
q.pop(); // 删除队首元素
q.size(); // 返回元素个数
q.empty(); // 判断是否为空
特别注意pop()的返回值是void而非元素值,这是为了避免不必要的拷贝构造。安全访问模式应该是:
cpp复制if(!q.empty()) {
auto val = q.front();
q.pop();
// 处理val...
}
queue作为容器适配器,允许指定底层容器类型:
cpp复制queue<int, list<int>> listQueue; // 基于list实现
queue<int, deque<int>> dequeQueue; // 显式指定deque(默认)
选择依据:
cpp复制struct Message {
int priority;
string content;
time_t timestamp;
};
queue<Message> msgQueue;
// 生产者线程
void producer() {
while(true) {
Message msg = getMessage(); // 获取消息
msgQueue.push(msg);
this_thread::sleep_for(100ms);
}
}
// 消费者线程
void consumer() {
while(true) {
if(!msgQueue.empty()) {
processMessage(msgQueue.front());
msgQueue.pop();
}
}
}
cpp复制void bfs(vector<vector<int>>& graph, int start) {
vector<bool> visited(graph.size(), false);
queue<int> q;
q.push(start);
visited[start] = true;
while(!q.empty()) {
int current = q.front();
q.pop();
for(int neighbor : graph[current]) {
if(!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
}
对于高频队列操作,可以预分配内存:
cpp复制queue<Data> highSpeedQueue;
vector<Data> buffer(100000); // 预分配内存
// 使用内存池技术
for(auto& item : buffer) {
highSpeedQueue.push(move(item));
}
cpp复制template<typename T>
class LockFreeQueue {
struct Node {
shared_ptr<T> data;
atomic<Node*> next;
};
atomic<Node*> head;
atomic<Node*> tail;
public:
void push(T new_value) {
shared_ptr<T> new_data = make_shared<T>(move(new_value));
Node* new_node = new Node;
new_node->data = new_data;
Node* old_tail = tail.load();
while(!old_tail->next.compare_exchange_weak(nullptr, new_node)) {
old_tail = tail.load();
}
tail.compare_exchange_weak(old_tail, new_node);
}
shared_ptr<T> pop() {
Node* old_head = head.load();
while(old_head && !head.compare_exchange_weak(old_head, old_head->next)) {
old_head = head.load();
}
return old_head ? old_head->data : shared_ptr<T>();
}
};
虽然queue本身不提供迭代器,但底层容器可能产生的问题:
cpp复制queue<int, vector<int>> badQueue; // 危险!
badQueue.push(1);
badQueue.push(2);
// 此时执行pop()会导致vector元素移动,任何保存的指针/引用都会失效
标准queue非线程安全,正确同步方式:
cpp复制mutex queueMutex;
condition_variable cv;
queue<Data> sharedQueue;
// 生产者
void producer() {
lock_guard<mutex> lock(queueMutex);
sharedQueue.push(getData());
cv.notify_one();
}
// 消费者
void consumer() {
unique_lock<mutex> lock(queueMutex);
cv.wait(lock, []{ return !sharedQueue.empty(); });
auto data = sharedQueue.front();
sharedQueue.pop();
}
使用gprof分析队列操作热点:
gprof <executable>对于内存分析,可使用valgrind:
bash复制valgrind --tool=memcheck --leak-check=full ./your_program
虽然不属于queue直接范畴,但priority_queue常与queue配合使用:
| 特性 | queue | priority_queue |
|---|---|---|
| 元素顺序 | FIFO | 按优先级排序 |
| 插入复杂度 | O(1) | O(log n) |
| 删除复杂度 | O(1) | O(log n) |
| 底层实现 | 默认deque | 默认vector+heap |
| 典型应用 | BFS、消息队列 | 任务调度、Dijkstra算法 |
转换示例:
cpp复制priority_queue<int> maxHeap; // 大顶堆
priority_queue<int, vector<int>, greater<int>> minHeap; // 小顶堆
cpp复制queue<unique_ptr<Resource>> resourceQueue;
auto res = make_unique<Resource>();
resourceQueue.push(move(res)); // 转移所有权
cpp复制queue<pair<int, string>> complexQueue;
complexQueue.emplace(42, "answer"); // 原地构造
cpp复制queue<tuple<int, double, string>> tupleQueue;
tupleQueue.emplace(1, 3.14, "pi");
auto [id, value, name] = tupleQueue.front();
理解STL queue的最好方式是自己实现一个简化版:
cpp复制template<typename T, typename Container = deque<T>>
class SimpleQueue {
protected:
Container c;
public:
using value_type = typename Container::value_type;
using size_type = typename Container::size_type;
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
value_type& front() { return c.front(); }
const value_type& front() const { return c.front(); }
value_type& back() { return c.back(); }
const value_type& back() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void push(value_type&& x) { c.push_back(move(x)); }
template<typename... Args>
void emplace(Args&&... args) {
c.emplace_back(forward<Args>(args)...);
}
void pop() { c.pop_front(); }
bool operator==(const SimpleQueue& other) const {
return c == other.c;
}
// 其他比较操作类似...
};
完善的单元测试应该覆盖:
cpp复制void testQueue() {
queue<int> q;
// 测试空队列行为
assert(q.empty());
assert(q.size() == 0);
// 测试基本操作
q.push(42);
assert(!q.empty());
assert(q.front() == 42);
assert(q.back() == 42);
q.push(100);
assert(q.front() == 42);
assert(q.back() == 100);
q.pop();
assert(q.front() == 100);
// 测试移动语义
queue<string> strQueue;
string s = "hello";
strQueue.push(move(s));
assert(s.empty());
assert(strQueue.front() == "hello");
// 测试自定义容器
queue<int, list<int>> listQueue;
listQueue.push(10);
assert(listQueue.front() == 10);
}
使用标准库的chrono进行简单性能测试:
cpp复制void benchmark() {
const int N = 1000000;
queue<int> q;
auto start = chrono::high_resolution_clock::now();
// 连续插入测试
for(int i=0; i<N; ++i) {
q.push(i);
}
auto mid = chrono::high_resolution_clock::now();
// 连续删除测试
while(!q.empty()) {
q.pop();
}
auto end = chrono::high_resolution_clock::now();
cout << "Insertion time: "
<< chrono::duration_cast<chrono::milliseconds>(mid-start).count()
<< "ms\n";
cout << "Deletion time: "
<< chrono::duration_cast<chrono::milliseconds>(end-mid).count()
<< "ms\n";
}
典型输出结果(仅供参考):
code复制Insertion time: 25ms
Deletion time: 15ms
不同标准库实现可能有细微差异:
编写可移植代码的建议:
cpp复制static_assert(is_same_v<
decltype(queue<int>().c)::value_type,
int>, "Underlying container type mismatch");
当标准queue不满足需求时,可以考虑:
boost::lockfree::queue:真正的无锁实现
moodycamel::ConcurrentQueue:高性能并发队列
TBB concurrent_queue:Intel线程构建块
选择建议:
队列在以下模式中扮演关键角色:
生产者-消费者模式
cpp复制template<typename T>
class Channel {
queue<T> buffer;
mutex mtx;
condition_variable cv;
size_t capacity;
public:
void send(T item) {
unique_lock<mutex> lock(mtx);
cv.wait(lock, [this]{ return buffer.size() < capacity; });
buffer.push(move(item));
cv.notify_one();
}
T receive() {
unique_lock<mutex> lock(mtx);
cv.wait(lock, [this]{ return !buffer.empty(); });
T item = move(buffer.front());
buffer.pop();
cv.notify_one();
return item;
}
};
管道过滤器模式
cpp复制template<typename T>
class Pipeline {
vector<queue<T>> stages;
vector<thread> workers;
void worker(size_t stage) {
while(!done) {
if(!stages[stage].empty()) {
auto data = stages[stage].front();
stages[stage].pop();
process(data);
stages[stage+1].push(data);
}
}
}
public:
void start() {
for(size_t i=0; i<stages.size()-1; ++i) {
workers.emplace_back(&Pipeline::worker, this, i);
}
}
};
以deque为基础的queue内存结构:
code复制Deque块布局:
+---+---+---+---+
| A | B | C | D | (每个块包含固定数量元素)
+---+---+---+---+
↑ ↑
front back
Queue视角:
[ A1, A2, ..., An, B1, B2, ..., Bn ]
↑ ↑
front back
关键观察:
使用gdb检查内存布局:
gdb复制(gdb) p *(std::queue<int>*)0x7fffffffdbe0
$1 = {
c = {
_M_impl = {
_M_map = 0x405030,
_M_map_size = 8,
_M_start = {
_M_cur = 0x405130,
_M_first = 0x405130,
_M_last = 0x405150,
_M_node = 0x405028
},
_M_finish = {
_M_cur = 0x405134,
_M_first = 0x405130,
_M_last = 0x405150,
_M_node = 0x405028
}
}
}
}
STL queue提供以下异常安全保证:
push操作:
pop操作:
front/back访问:
编写异常安全代码的实践:
cpp复制void safeProcessing(queue<Resource>& q) {
if(q.empty()) return;
try {
Resource res = q.front(); // 可能抛出拷贝异常
q.pop(); // 不会抛出
process(res); // 处理资源
}
catch(...) {
// 即使发生异常,queue状态也是确定的
logError("Processing failed");
}
}
为queue指定内存分配器:
cpp复制template<typename T>
class CustomAllocator {
public:
using value_type = T;
T* allocate(size_t n) {
auto p = static_cast<T*>(malloc(n * sizeof(T)));
if(!p) throw bad_alloc();
return p;
}
void deallocate(T* p, size_t) noexcept {
free(p);
}
};
queue<int, deque<int, CustomAllocator<int>>> customQueue;
高级用法——内存池分配器:
cpp复制template<typename T, size_t PoolSize = 1024>
class PoolAllocator {
struct Block {
alignas(T) unsigned char data[sizeof(T)];
bool used = false;
};
static Block pool[PoolSize];
public:
T* allocate(size_t n) {
if(n != 1) throw bad_alloc();
for(auto& block : pool) {
if(!block.used) {
block.used = true;
return reinterpret_cast<T*>(&block.data);
}
}
throw bad_alloc();
}
void deallocate(T* p, size_t) {
auto block = reinterpret_cast<Block*>(
reinterpret_cast<unsigned char*>(p) - offsetof(Block, data));
block->used = false;
}
};
将queue内容序列化的常见方案:
cpp复制void serialize(const queue<int>& q, ostream& os) {
size_t size = q.size();
os.write(reinterpret_cast<char*>(&size), sizeof(size));
queue<int> temp = q;
while(!temp.empty()) {
int val = temp.front();
os.write(reinterpret_cast<char*>(&val), sizeof(val));
temp.pop();
}
}
queue<int> deserialize(istream& is) {
queue<int> q;
size_t size;
is.read(reinterpret_cast<char*>(&size), sizeof(size));
while(size--) {
int val;
is.read(reinterpret_cast<char*>(&val), sizeof(val));
q.push(val);
}
return q;
}
cpp复制void to_json(json& j, const queue<int>& q) {
queue<int> temp = q;
j = json::array();
while(!temp.empty()) {
j.push_back(temp.front());
temp.pop();
}
}
queue<int> from_json(const json& j) {
queue<int> q;
for(auto& item : j) {
q.push(item.get<int>());
}
return q;
}
编译时队列特性检测:
cpp复制template<typename T>
constexpr bool is_queue_v = false;
template<typename T, typename Container>
constexpr bool is_queue_v<queue<T, Container>> = true;
// 使用示例
static_assert(is_queue_v<queue<int>>, "Must be a queue type");
队列元素类型萃取:
cpp复制template<typename T>
struct queue_value_type {
using type = void;
};
template<typename T, typename Container>
struct queue_value_type<queue<T, Container>> {
using type = T;
};
template<typename T>
using queue_value_type_t = typename queue_value_type<T>::type;
cpp复制template<typename T, typename Container>
ostream& operator<<(ostream& os, const queue<T, Container>& q) {
os << "queue[";
auto temp = q;
bool first = true;
while(!temp.empty()) {
if(!first) os << ", ";
os << temp.front();
temp.pop();
first = false;
}
return os << "]";
}
// 使用示例
queue<int> q{1,2,3};
cout << q; // 输出: queue[1, 2, 3]
使用perf工具分析Linux下的队列性能:
bash复制perf record -g ./queue_benchmark
perf report -g "graph,0.5,caller"
关键指标关注点:
cpp复制template<typename Q>
concept QueueType = requires(Q q, typename Q::value_type x) {
{ q.push(x) } -> same_as<void>;
{ q.front() } -> convertible_to<typename Q::value_type>;
{ q.pop() } -> same_as<void>;
{ q.empty() } -> convertible_to<bool>;
{ q.size() } -> convertible_to<size_t>;
};
template<QueueType Q>
void processQueue(Q& q) {
// 安全处理任何符合队列概念的类型
}
cpp复制async_generator<int> queueProducer(queue<int>& q) {
while(!q.empty()) {
int val = q.front();
q.pop();
co_yield val;
}
}
async_task<void> queueConsumer(queue<int>& q) {
auto gen = queueProducer(q);
while(auto val = co_await gen) {
process(*val);
}
}