循环队列
循环队列(共享内存)笔记
一、为什么共享内存不能使用 STL queue
在多进程共享内存开发中,不能直接使用 STL 的 std::queue,原因如下:
-
STL 容器使用动态内存分配(new/delete),共享内存无法跨进程管理堆内存
-
依赖移动语义、动态扩容机制,不适合固定内存场景
-
共享内存中的对象不会调用构造/析构函数,STL 容器无法正常初始化
解决方案:手写固定大小数组循环队列
所有成员变量为普通内置类型,无动态内存,可以直接放入共享内存。
二、自定义循环队列整体设计思想
采用 数组 + head + tail + length 计数法实现循环队列:
-
m_head:队头下标(出队位置)
-
m_tail:队尾下标(入队位置)
-
m_length:当前队列元素个数(无需空位置判断)
-
m_data[]:固定大小数组存储数据
-
m_inited:初始化标记(适配共享内存)
三、头文件 _public.h
类声明、函数原型、模板定义
#ifndef __PUBLIC_HH
#define __PUBLIC_HH#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
using namespace std;// 适配共享内存的固定大小循环队列
template <class TT, int MaxLength>
class squeue
{
private:bool m_inited;TT m_data[MaxLength];int m_head;int m_tail;int m_length;// 禁止拷贝构造和赋值(共享内存必须)squeue(const squeue &) = delete;squeue &operator=(const squeue &) = delete;public:squeue();void init(); // 初始化队列bool empty(); // 判断空bool full(); // 判断满int size(); // 当前元素个数TT &front(); // 获取队头元素bool push(const TT &ee); // 入队bool pop(); // 出队void printqueue(); // 打印队列(调试)
};#endif
四、源文件 _public.cpp
所有成员函数实现 + 模板类显式实例化(关键)
#include "_public.h"// 构造函数
template <class TT, int MaxLength>
squeue<TT, MaxLength>::squeue()
{init();
}// 初始化队列(共享内存必须手动调用)
template <class TT, int MaxLength>
void squeue<TT, MaxLength>::init()
{if (m_inited != true){m_head = 0;m_tail = MaxLength - 1;m_length = 0;memset(m_data, 0, sizeof(m_data));m_inited = true;}
}// 判断队列是否为空
template <class TT, int MaxLength>
bool squeue<TT, MaxLength>::empty()
{return m_length == 0;
}// 判断队列是否已满
template <class TT, int MaxLength>
bool squeue<TT, MaxLength>::full()
{return m_length == MaxLength;
}// 获取元素个数
template <class TT, int MaxLength>
int squeue<TT, MaxLength>::size()
{return m_length;
}// 获取队头元素(不出队)
template <class TT, int MaxLength>
TT &squeue<TT, MaxLength>::front()
{return m_data[m_head];
}// 元素入队
template <class TT, int MaxLength>
bool squeue<TT, MaxLength>::push(const TT &ee)
{if (full()){cout << "循环队列已满,入队失败。" << endl;return false;}m_tail = (m_tail + 1) % MaxLength;m_data[m_tail] = ee;m_length++;return true;
}// 元素出队
template <class TT, int MaxLength>
bool squeue<TT, MaxLength>::pop()
{if (empty()){cout << "循环队列为空,出队失败。" << endl;return false;}m_head = (m_head + 1) % MaxLength;m_length--;return true;
}// 打印所有队列元素(调试)
template <class TT, int MaxLength>
void squeue<TT, MaxLength>::printqueue()
{for (int ii = 0; ii < size(); ii++){int idx = (m_head + ii) % MaxLength;cout << "m_data[" << idx << "], value=" << m_data[idx] << endl;}
}// 模板类显式实例化(必须,否则编译不通过)
template class squeue<int, 5>;
五、测试代码 demo1.cpp
#include "_public.h"int main()
{using ElemType = int;squeue<ElemType, 5> QQ;ElemType ee;cout << "元素(1、2、3)入队。" << endl;ee = 1; QQ.push(ee);ee = 2; QQ.push(ee);ee = 3; QQ.push(ee);cout << "队列的长度是 " << QQ.size() << endl;QQ.printqueue();ee = QQ.front(); QQ.pop();cout << "出队的元素值为 " << ee << endl;ee = QQ.front(); QQ.pop();cout << "出队的元素值为 " << ee << endl;return 0;
}
六、Makefile
必须依赖 _public.cpp,模板类不能只依赖头文件
all: demo1demo1: demo1.cpp _public.h _public.cppg++ -g -o demo1 demo1.cpp _public.cppclean:rm -f demo1
七、拓展:STL queue 原生结构
template <class T, class _Container = deque<T>>
class queue
{};
STL queue 常用接口:
-
push():元素入队
-
emplace():原地构造元素入队
-
size():获取元素个数
-
empty():判断队列是否为空
-
front():队头元素
-
back():队尾元素
八、核心知识点总结
-
自定义循环队列无动态内存,适配共享内存
-
使用 m_length 计数法,不牺牲存储空间
-
共享内存中的对象不会自动初始化,必须手动调用
init() -
模板类必须在 cpp 文件显式实例化,否则链接失败
-
禁止拷贝构造、赋值重载,保证共享内存安全
