不能只用 std::queue 实现 FIFO 页面置换,因其不支持查找,无法高效判断页面是否命中;需配合 std::unordered_set 维护页集合以实现 O(1) 命中检测,并同步更新队列与集合。

直接用 std::queue 模拟 FIFO 页面置换是可行的,但**不能只靠它完成命中判断和淘汰逻辑**——因为 std::queue 不支持遍历或查找,你每次访问页面时无法快速知道它是否已在内存中。
为什么不能只用 std::queue 做 FIFO 置换
FIFO 的核心动作有两步:1)查页是否在内存里;2)若不在且内存满,则弹出队首、插入新页。但 std::queue 只提供 front()、back()、push()、pop(),没有 find() 或迭代器暴露机制。
- 如果你每次查命中都把整个队列倒出来再塞回去,时间复杂度是 O(n),实测在 320 次访问 + 3 物理块下会慢 5–8 倍
-
std::queue底层默认是std::deque,虽支持随机访问,但接口被封装掉了,强行转型或访问内部容器属于未定义行为 - 很多教学代码用
vector+ 手动维护头尾索引,看似“不用 STL”,实则更易越界、难调试
推荐组合:std::queue + std::unordered_set
用 std::queue 维护页面进入顺序,用 std::unordered_set 维护当前内存中的页号集合——两者同步更新,查命中只需 O(1) 平均时间。
- 插入新页时:
queue.push(page)+set.insert(page) - 淘汰页时:
int victim = queue.front(); queue.pop(); set.erase(victim) - 查命中:
if (set.find(page) != set.end()),比遍历数组快一个数量级 - 注意:
std::unordered_set要求页号类型可哈希,int默认支持,无需额外操作
命中率计算容易漏掉的边界
命中率不是简单用「命中次数 / 总访问次数」,必须明确「缺页中断」才触发计数,而「命中」不改变物理块内容,也不计入缺页。
- 初始时物理块为空,第一次访问任何页都必然缺页,此时要先加载、再计数
- 如果物理块大小为 0(极端输入),所有访问都缺页,但程序应提前报错而非崩溃
- 测试序列含重复相邻页(如
0 0 0),第二个及之后的0是命中,但部分实现因忘记清空中间状态误判为缺页 - 输出缺页率时建议用
printf("%.2f%%", 100.0 * cnt_miss / n),避免整数除法截断(如5/13直接得 0)
完整可运行的最小 FIFO 实现要点
以下逻辑足够跑通标准测试用例(如访问串 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1,块数=3):
- 输入前先校验物理块数
m > 0且访问序列非空 - 用
std::queue存驻留页顺序,std::unordered_set存驻留页集合 - 遍历每个访问页:
if (set.count(page))→ 命中;否则 → 缺页,检查queue.size() 决定是否淘汰 - 淘汰时必须同时从
queue和set移除同一页面,顺序不能颠倒(先erase再pop会导致set残留) - 最后输出缺页次数、缺页率、命中率三个值,别只打一个
真正卡住人的从来不是算法思想,而是 set 和 queue 同步时机、空块处理、浮点输出精度这三处细节。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











