
简介QT停车场管理系统是一套基于Qt框架开发的图形化停车场管理程序模拟商场地下停车场场景适合正在学习C/Qt编程及数据结构应用的开发者参考。程序设计了入口、出口与入口前便道车位占满时车辆可在便道排队等候内部车位采用数组表示便道则用队列实现先进先出覆盖入库、出库、查询等核心功能。资源包共11个文件压缩后大小约12KB包含3个cpp源文件、2个头文件、2个ui界面文件以及pro工程文件、qrc资源、png图片和用户配置等整体工程结构清晰模块划分明确便于快速理解Qt项目的组织方式。目前已有3963人浏览学习下载后可直接编译运行也可在此基础上扩展界面或功能可作为课程设计或期末项目参考是练习图形界面与数据结构综合应用的实用范例。1. QT停车场管理系统用数组和队列就能跑起来的图形界面课设做 Qt 课设的人十有八九会卡在“界面做出来了业务逻辑不知道怎么组织”这一步上。这套 QT 停车场管理系统恰好是反着来的——它把最难的数据结构部分用一句话点透了停车场内部不用栈、不要求先来先走直接开一个数组就能存便道排队才必须用队列先到的车先进车库。整个包只有十几个文件主逻辑集中在 widget.cpp 和 in.cpp 里配合 parkroom.pro 编译即可运行。适合刚学完 Qt 基础、想找一个带 QPainter 绘图和信号槽交互的完整项目来练手的人也适合需要在一周内拿出一份可演示课设的从业者代码量不大逻辑能讲清楚答辩时反而好说。2. 数据模型先定死车位用数组、便道用队列为什么不能反过来2.1 停车场内部不要求“先来先走”栈和队列反而碍事先看摘要里最容易被忽略的一句话“停车场内部不要求先来先走也不要求先来后走所以停车场内部可以不用堆栈也可以不用队列设置一个数组即可。” 这不是偷懒而是对真实地下停车场行为的还原。大多数商场地库对车位没有任何强制顺序约束一辆车开进去停在哪个位是随缘的取车时也不存在“后进必须先出”的规则——谁的车谁开走。栈的行为是 LIFO队列的行为是 FIFO这两个结构都强加了严格的出入次序。如果代码里用一个栈出库时就会被迫弹出最后停入的车完全不符合现实。用一个数组就自由多了每个下标对应一个车位标识“空/占用”就行出库时只要按下标释放即可不需要关心其他车位的顺序。数组还能天然支持“查询某个车位有没有车”这种高频操作时间复杂度 O(1)而栈和队列想查中间某个元素得遍历。2.2 便道为什么必须是队列先到先得是入场的唯一公平规则便道场景和内部正好相反。入口前只有一条单行道闸机一次只能放一辆车进去排在前面的人先入场这是硬性规则插队就是矛盾。摘要里明确写了“便道必须是队列先来等候的车先进停车场”这里用 C 手写一个顺序队列即可不必引入 STL 的 std::queue因为课程设计要求能讲清底层实现。推荐用环形队列避免“假上溢”。线性队列每次出队 head 往后挪到尾部就无法再入队即使前面空着。环形队列让 tail 在到达数组末尾时回绕到下标 0把 WAIT_MAX 个空间循环利用。队列需要三个成员head 指向队首、tail 指向下一个可用位置、count 记录当前排队车辆数。判断满的条件是 count WAIT_MAX而不是 head tail——后者在“空”和“满”两种状态下都成立。2.3 参数怎么定车位数和便道容量先写死再留一个可调入口封面说“自己定义停车场内部的车位数便道上的等候数”实际落地时我建议在 widget.h 顶部用两个常量集中定义不要散落在业务代码里#define PARK_SLOTS 5 // 停车场内部车位总数 #define WAIT_MAX 3 // 便道最大等候车辆数 struct ParkSlot { bool occupied; // true 表示该车位有车 int carId; // 车辆编号入库时从 1 开始自增 time_t enterTime; // 入库时间戳后续计时计费用 }; struct WaitQueue { int data[WAIT_MAX]; // 存储等待车辆的 carId int head; // 队首下标 int tail; // 下一个可写入的下标 int count; // 当前排队数量 };逻辑说明ParkSlot 结构体占一个普通数组下标 0 到 PARK_SLOTS-1 就是车位的编号。carId 是整个系统里唯一标识一辆车的编号入场时分配出库时释放enterTime 先用 time_t 存原始时间戳界面展示时再转成字符串。WaitQueue 里只存 carId 而不是整辆车的完整信息因为车辆详细信息入场时间等已经交给车位数组管理了队列只需要知道“谁在等”这样可以省掉一份冗余数据。参数说明PARK_SLOTS 和 WAIT_MAX 是演示模式下写死的值。实际运行时如果想从配置文件读取可以把这两个宏替换成成员变量在构造函数里用 QSettings 初始化。但从课设答辩的角度看宏定义更直白老师一眼就能看到内部 5 个车位、便道最多排 3 辆车的设定。另外建议 PARK_SLOTS 不要小于 3否则“满库 便道 空位释放后人库进车”这条完整链路演示不出来。2.4 队列的入队出队函数边界条件的返回值不能含糊手写队列最容易翻车的点在“空/满”判断。下面提供的是一套可以直接抄进 widget.h 的接口声明和实现bool queue_push(WaitQueue* q, int carId); // 入队成功返回 true队满返回 false bool queue_pop(WaitQueue* q, int* outId); // 出队成功返回 true队空返回 false bool queue_isEmpty(const WaitQueue* q); // 队空判断 bool queue_isFull(const WaitQueue* q); // 队满判断 bool queue_push(WaitQueue* q, int carId) { if (!q || q-count WAIT_MAX) return false; q-data[q-tail] carId; q-tail (q-tail 1) % WAIT_MAX; q-count; return true; } bool queue_pop(WaitQueue* q, int* outId) { if (!q || q-count 0) return false; *outId q-data[q-head]; q-head (q-head 1) % WAIT_MAX; q-count--; return true; }逻辑说明push 时先判满写入 tail 位置后移动尾指针模运算让下标自动回绕pop 时先判空从 head 取出值再移动头指针。这两段代码看着简单但 return false 的时机必须严格统一。在业务层调用时入库逻辑会先检查车库是否已满如果已满就调用 push如果 push 返回 false 说明便道也满了这时才能向用户提示“车库和便道都已满请稍后再来”。参数说明指针传参是因为函数需要同时修改 head、tail、count 三个值C 风格代码里用指针最省事如果项目整体用 Qt 风格也可以改成 WaitQueue 的成员函数把 head/tail/count 设为私有字段。outId 在 pop 里是输出参数调用方不需要先初始化它函数内部在队非空时才会写入。返回 bool 而不是 void是为了在主流程里省掉一次冗余的 isEmpty 检查直接把出队结果作为 if 条件用。3. 项目结构拆解从 parkroom.pro 到 widget.ui界面和资源怎么组织3.1 这套项目的文件分工哪些是 Qt 生成、哪些要手写、哪些能删看压缩包里的清单parkroom.pro 是 Qt 工程文件parkroom.pro.user 是 Qt Creator 的用户级配置可以很肯定地说——pro.user 属于本机配置文件换台电脑打开工程时会被 Qt Creator 自动重建真正需要阅读和修改的是 parkroom.pro。widget.cpp、widget.h、widget.ui 组成主窗口in.cpp、in.cpp、in.ui 负责入库操作的相关逻辑res.qrc 统一管理资源文件qiche.png 就是热词里常刷到的“qt绘图”用的车辆素材图。建议启动项目前先确认 .pro 文件里有没有把所有源文件列全。一个完整可编译的 parkroom.pro 至少长这样QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET parkroom TEMPLATE app DEFINES QT_DEPRECATED_WARNINGS SOURCES \ main.cpp \ widget.cpp \ in.cpp HEADERS \ widget.h \ in.h FORMS \ widget.ui \ in.ui RESOURCES \ res.qrc逻辑说明SOURCES 列表里丢了任何一个 .cpp编译就会报“undefined reference to vtable for Widget”一类的链接错误但这个错误出现时大家的第一反应往往是“是不是 Qt 库没装对”实际检查清单顺序应该是先看 .pro。QT core gui 是 5.x 版本的基础模块如果代码里用了 QPaintergui 模块已经涵盖如果用到 QProcess 或 QTcpSocket 这类网络功能再往这里加对应的模块名。FORMS 里列的是 .ui 文件它最终会被 uic 工具编译成 ui_widget.h这个生成过程发生在编译阶段不需要手动 include。参数说明TARGET 指定生成的 exe 名称这里叫 parkroom那么 build 目录下最终产出的就是 parkroom.exe。TEMPLATE app 表示生成应用程序不是静态库。DEFINES 里那行 DEPRECATION 警告是 Qt 的惯例保留着反而能让编译输出窗口提示你哪些 API 不该在新项目里用。3.2 widget.ui 和 in.ui 的职责划分主窗口只管场地图入库用独立对话框widget.ui 是主界面建议放一个自定义绘图区域 “入库”“出库”“查询”“模拟数据”四个按钮。in.ui 是入库对话框只有一个“车牌号输入框”和“确定/取消”两个按钮。这样设计的好处是业务场景解耦主窗口负责画车位、画便道、画小车对话框只负责收集入库请求里的车辆标识确认后把数据传回主窗口。在 Qt Designer 里操作时不需要把界面设计得特别复杂。widget.ui 里可以直接放一个空的 QWidget 作为绘图画布然后提升promote成自定义类。常见的做法是新建一个 ParkWidget重写它的 paintEvent 来绘制停车场全景。如果你想让代码更集中也可以不提升直接在 widget.cpp 里重写 QWidget 的 paintEvent——但那样 widget.cpp 会膨胀到几百行答辩时讲解节奏会被拖慢。in.ui 里如果放一个 QLineEdit 用来输入车牌在 widget.cpp 里这样接收值#include in.h void Widget::on_btnIn_clicked() { InDialog dlg(this); if (dlg.exec() QDialog::Accepted) { QString plate dlg.getPlate(); handleIncomingCar(plate); } }逻辑说明exec() 是模态方式打开对话框用户点“确定”后返回 Accepted点“取消”返回 Rejected。只有 Accepted 分支才执行真正的入库逻辑这样用户在弹窗里填了一半取消不会污染停车场的状态。getPlate() 是 InDialog 对外开放的取值函数内部实现就是 lineEdit-text().trimmed()。3.3 把 qiche.png 编进 res.qrc路径前缀和引用名先对齐qiche.png 是压缩包里自带的车辆素材图片热词里“qt绘图”“qt界面设计”都绕不开资源系统的配置。资源文件 res.qrc 的写法是 XML 格式RCC qresource prefix/ fileimage/qiche.png/file /qresource /RCC逻辑说明prefix 是“/”实际代码里引用这张图时写作 QPixmap(:/image/qiche.png)。注意冒号开头表示这是 Qt 资源系统内部路径不是磁盘上的相对路径。很多人在这一步踩坑——他们直接写 image/qiche.png 少了冒号和斜杠QPixmap 构造出来的对象是空的paintEvent 里 drawPixmap 就什么都不画。如果你把 qiche.png 直接放在 res.qrc 的同级目录而不是 image 子目录那 qresource 里的路径也要同步改成 qiche.png 。参数说明qresource 的 prefix 可以设置成任意名字比如 prefix/pres那么引用路径就要写 :/pres/image/qiche.png。不值得为了省几个字符去改默认 / 风格通用性最好以后加别的图片资源不用额外再配前缀。3.4 main.cpp 入口高 DPI 适配和窗口尺寸的初始化#include widget.h #include QApplication int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }逻辑说明这是 Qt Widgets 应用的标准入口没有任何业务逻辑。QApplication 负责事件循环和全局资源管理Widget 是主窗体show() 之后进入 exec() 事件循环。如果要在 Windows 上做高分屏适配在创建 QApplication 之前加一句 qputenv(QT_ENABLE_HIGHDPI_SCALING, 1)否则在 2K/4K 分辨率的屏幕上控件会显得很小。这个细节在写课设报告时可以作为“界面适配”的加分项提一句。参数说明argc 和 argv 是命令行参数Qt 内部会解析一些以 -q 开头的参数比如 -stylesheet 可以指定界面样式文件普通程序不需要处理这些原样透传给 QApplication 即可。4. 入库出库与查询状态流转怎么实现界面刷新靠什么机制4.1 入库的完整判断链内部有空位 便道有空位 全部满员入库不是简单地把数据塞进去而是先判断当前系统处于哪个状态依次处理“直接入场”“便道排队”“拒绝入场”三种情况。这段代码直接放在 widget.cpp 里void Widget::handleIncomingCar(const QString plate) { // 1. 先看停车场内部是否还有空闲车位 for (int i 0; i PARK_SLOTS; i) { if (!slots[i].occupied) { slots[i].occupied true; slots[i].carId globalCarId; slots[i].enterTime time(nullptr); plateMap[globalCarId] plate; logMessage(QString(车辆 %1 入场停入 %2 号车位).arg(plate).arg(i)); update(); return; } } // 2. 内部已满尝试进入便道队列 int newId globalCarId; if (queue_push(waitQueue, newId)) { plateMap[newId] plate; logMessage(QString(车辆 %1 已满进入便道排队当前排队 %2 辆) .arg(plate).arg(waitQueue.count)); } else { globalCarId--; // 入队失败编号回退 logMessage(车库和便道均已满拒绝入场); } update(); }逻辑说明第一段循环检查车位数组一旦遇到 occupied false 就直接占位不需要继续查找因为“任意空位都可以停”是题目本来就允许的。carId 用自增方式生成保证唯一。time(nullptr) 打点为后续计时做准备。第二段在内部满员时转投便道但要注意queue_push 成功才把车牌写进 plateMap失败时需要把 globalCarId 回退否则编号会留下空洞出库时按编号查车牌会查不到。参数说明plateMap 是 QMapint, QString它的作用是把 carId 和车牌号绑定。这里绕过了一个设计陷阱ParkSlot 里只存了 carId 而没有直接存车牌号因为车辆可能在便道排队时还没有车位等待期间车牌需要有一处存放地plateMap 就是统一存放处等它从便道进入车位数组时再把 carId 写入 ParkSlot。优点是所有车辆的 carId-plate 关系都集中在一个容器里出库时只需要 carId 就能反查车牌。4.2 出库流程找到车、释放车位、唤醒便道第一辆车出库最关键的隐性逻辑是“空出的车位要让便道队首的车补进来”这是整个程序里最容易漏写的一步。只释放车位不调度便道便道队列就永远不会前进。void Widget::handleOutgoingCar(int carId) { // 1. 在车位数组里找到 carId 对应位置释放 for (int i 0; i PARK_SLOTS; i) { if (slots[i].occupied slots[i].carId carId) { slots[i].occupied false; slots[i].carId -1; slots[i].enterTime 0; break; } } // 2. 如果有便道等待车辆让其立即入场 if (!queue_isEmpty(waitQueue)) { int newCarId 0; queue_pop(waitQueue, newCarId); for (int i 0; i PARK_SLOTS; i) { if (!slots[i].occupied) { slots[i].occupied true; slots[i].carId newCarId; slots[i].enterTime time(nullptr); break; } } logMessage(QString(便道车辆 %1 进入 %2 号车位).arg(plateMap[newCarId]).arg(i)); } // 3. 清理离场车辆的车牌记录 plateMap.remove(carId); update(); }逻辑说明释放车位的操作是标准的“找到-标记-重置”。找车位用线性遍历因为 PARK_SLOTS 默认只有 5性能上没有任何问题。真正值得关注的是第二步pop 出来的 newCarId 要重新找一遍空位。这里假设了“刚释放的车位一定会被便道车辆补上”这是合理的因为出库动作必然腾出一个空位。车牌记录的清理放到最后一步是因为第二步补位时 plateMap[newCarId] 还要用来打印日志先删旧记录会导致取不到便道车辆的车牌。参数说明carId 参数从哪里来在界面上我建议用一个 QListWidget 展示当前场内车辆列表用户点击某一项后拿到对应 carId再调用 handleOutgoingCar。如果要在查询模式下操作那就换成表格控件 QTableWidget。核心原则是不用手动输入车牌避免字符串匹配带来的大小写和空格问题——车牌从 plateMap 里取永远可靠。4.3 状态查询和界面刷新update() 触发 paintEvent日志记录全靠信号槽查询功能分两部分出租车位状态信息和便道排队状态。不用单独开线程直接在按钮点击事件里拼接字符串给用户看即可。真正对界面形态有影响的是 update() 调用——它告诉 Qt“这个控件需要重绘”随后 Qt 在下一轮事件循环中调用 paintEvent。void Widget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); // 画所有车位格子 for (int i 0; i PARK_SLOTS; i) { QRect rect(50 i * 120, 100, 100, 180); painter.drawRect(rect); if (slots[i].occupied) { painter.drawPixmap(rect, QPixmap(:/image/qiche.png)); painter.drawText(rect.left() 10, rect.top() 160, plateMap.value(slots[i].carId)); } else { painter.drawText(rect, Qt::AlignCenter, 空车位); } } }逻辑说明paintEvent 是 QWidget 的虚函数绘制前先把每个车位用 drawRect 画成矩形占用状态就用 drawPixmap 贴车图同时用 drawText 把车牌打印在矩形下方。QPainter::Antialiasing 让矩形边缘平滑在高分屏上更清晰。这里的所有绘制都是即时读取 slots 数组的数据所以只要业务数据更新后调用 update()界面就能同步反映不用手写“刷新列表”的逻辑这是 Qt 双缓冲机制带来的方便。参数说明rect 的坐标是写死的50 i * 120 表示第 0 个车位在 x50 处每个车位横向间隔 120 像素格子宽 100高 180。如果菜单栏或标题栏占用了顶部空间y 坐标 100 可能被遮挡需要根据实际窗口尺寸调整。严格起见可以把车位坐标改成成员变量在 resizeEvent 里重新计算但课设项目这么做会让代码量翻倍收益不大。4.4 出入库信号槽连接方式连接不上到底是谁接歪了Qt 的信号槽有两种连接方式。Qt 5 之后推荐使用新式写法编译期就能发现函数名拼写错误老式写法用 SIGNAL/SLOT 宏运行期连接失败只会在控制台输出一行警告不仔细看就忽略过去了。这里两条都写便于对照// 新式写法编译器校验推荐 connect(btnIn, QPushButton::clicked, this, Widget::on_btnIn_clicked); // 旧式写法字符串匹配函数名打错不会报编译错误 connect(btnIn, SIGNAL(clicked()), this, SLOT(on_btnIn_clicked()));逻辑说明多数项目提供的代码是旧式写法因为老教程和模板都这么写。如果你打开 widget.cpp 发现编译不报错但按钮点了没反应第一件事就是用新式写法替换旧式。旧式写法里的函数参数不能带类型名比如 clicked() 而不是 clicked(bool)带括号里的类型就连接失败。另外注意按钮对象的父对象关系如果 btnIn 是成员变量且由代码 new 出来的要在 connect 前确保 new 完成。参数说明自定义信号也可以照葫芦画瓢比如在主窗口里声明 signal void carIn();在 handleIncomingCar 成功后 emit carIn()再连到状态栏的 updateStatusSlot()——这套 dispatch 方式适合以后把项目拆成 Model/View 结构但现在这个规模不需要。5. 避坑排查编译报错、图片消失与列车队死循环的 5 条血泪经验5.1 现象运行 exe 弹窗提示 qt.qpa.plugin: could not find the Qt platform plugin windows原因这是 Qt 最经典的发布期故障意思是程序找不到 platforms 目录下的 qwindows.dll。在 Qt Creator 里能跑起来是因为 IDE 设置了环境变量但脱离 IDE 双击 exe 后程序不知道 Qt 库在哪里。解决在 build 目录下打开命令行执行C:\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe parkroom.exe它会自动复制运行所需的 dll 和 plugins 目录。如果没装 windeployqt也可以手工拷贝 platforms/qwindows.dll 到 exe 旁边但那样容易少拷几个还是用工具一次到位。热词里搜“qt.qpa.plugin could not find the qt platform plugin”的开发者绝大多数最后都是靠这条命令解决的。5.2 现象编译输出窗口第一行就是:-1: error: dependent ..\..\..\qt\5.15.2\msvc2019_64\include\qtwidgets not found原因这个报错看着吓人其实是 Qt 的 include 路径失效了。最常见的原因是项目所在路径被移动过或者 .pro.user 文件里记录的是旧电脑的 Qt 安装路径新环境里 QT 目录不匹配。解决菜单栏选择“工具 → 选项 → Kits”确认当前使用的 Qt 版本路径和编译器套件一致。打开“项目 → 构建环境”检查 QTDIR 变量指向的目录是否真实存在。如果路径没问题删除 parkroom.pro.user 文件后重新打开 .proQt Creator 会重新生成本机配置。这个文件是文本格式你甚至可以打开它检查里面记录的 qt 路径是否还指向 C 盘某个已经被挪走的目录。别手改删掉重建最干净。5.3 现象widget.cpp 里 this-ui-btnIn 编译报错提示 class Ui::Widget 没有成员 btnIn原因.ui 文件里设计的按钮没保存或者保存后没有重新编译生成 ui_widget.h。Qt Creator 只在 .ui 文件修改保存时自动调用 uic如果改完 UI 直接点“运行”有时不会触发重新生成。解决对 widget.ui 所在目录执行一次“清理项目”再重新构建让 qmake 重新处理 UI 文件。还有一种情况 .pro 里 FORMS 没包含该 .ui那样 uic 根本不会碰它。查 FORMS 列表和 .ui 文件里的 objectName 是不是 btnIn注意 QLineEdit 的叫 editPlateQPushButton 的叫 btnIn别在代码里把 edit 的对象名当按钮用。这类问题属于“类的定义滞后于界面设计”不是代码思路错重建一遍就好。5.4 现象空车位显示正常但占用车位的车图不显示原因QPixmap(:/image/qiche.png) 构造了空 pixmapdrawPixmap 画上去一片空白。根源是 res.qrc 里的路径和代码引用的路径不一致比如 .qrc 里写的是 image/qiche.png 但文件实际放在根目录或者 qrc 文件没有在 .pro 的 RESOURCES 里登记。解决先用一个临时标签页验证资源是否加载成功QPixmap pm(:/image/qiche.png); qDebug() pm.isNull();输出 false 说明资源没问题问题在绘图逻辑输出 true 说明资源系统没加载或路径写错。对照 res.qrc 的 XML 层级逐级看记住 qrc 内部路径的参照点是 .qrc 文件所在目录。有一个隐藏坑文件名大小写Windows 上磁盘文件 qiche.png 和 qiche.PNG 都能打开但 Qt 资源的 alias 匹配是大小写敏感的。5.5 现象便道队列在排到第 3 辆车后程序陷入死循环或闪退原因队列数组的下标回绕逻辑出错。典型错误是 tail 加完后取模但对 full 的判断用了 tail head 而不是 count WAIT_MAX。环形队列在空和满两种状态下 tail 和 head 恰好都相等如果按 tailhead 判断满空队列会直接被认为已满入队操作永远失败业务层反复重试便卡死。解决严格使用 count 作为队列状态的唯一判据。上面第 2.3 节提供的 queue_push 和 queue_pop 实现已经把这一点固化。还需要在每次 push/pop 后检查 count 是否越界。一个更简单的防御手段是在调试时打印尾指针和头指针qDebug() head q-head tail q-tail count q-count;看到 head 和 tail 相同且 count 等于 0说明状态正常count 在 1 到 WAIT_MAX-1 之间时 head 和 tail 允许相等这正是环形队列的常规形态不必惊慌。真正要检查的是 count 是否超过 WAIT_MAX那一定意味着 push 的满判断出问题。6. 加一个计时收费模块让演示场景随时能验证完整业务闭环这套项目目前是纯“状态记录”逻辑入出库都不涉及费用。如果你拿去答辩老师大概率会追问一句“停车场系统怎么收费”。与其临时口述不如直接给代码补一个计时收费函数。实现思路很直接ParkSlot 里的 enterTime 已经记录了入场时间出库时用 time(nullptr) 减去它得到秒数按“每小时 X 元、不足一小时按一小时算”的规则结算。QString Widget::calcFee(int carId) { time_t now time(nullptr); for (int i 0; i PARK_SLOTS; i) { if (slots[i].occupied slots[i].carId carId) { int minutes (now - slots[i].enterTime) / 60; int hours (minutes 59) / 60; // 向上取整 double fee hours * 3.0; // 每小时3元 return QString(车辆 %1 停 %2 分钟共 %3 元) .arg(plateMap[carId]).arg(minutes).arg(fee); } } return 车辆不存在; }这里的核心是向上取整停 1 分钟也按 1 小时收 3 元(minutes 59) / 60 在整数除法下实现了这个效果。出库按钮点击时先调用 calcFee 弹窗展示费用确认后调用 handleOutgoingCar 执行释放。验证这套逻辑有一个很稳的习惯把 PARK_SLOTS 改成占用数减到 1再连续入库 5 辆不同车牌的车观察前 3 辆直接进场、第 4 辆进便道、第 5 辆拒绝入场然后手动出库一辆确认便道队首能自动补位。这个流程走完核心数据结构和界面刷新就都验证过了。从那以后我每次拿到新的 Qt 项目都会先清理构建目录、确认 .pro 的源文件清单再编译——这两个动作能免掉一半莫名其妙的编译错误。希望帮你少走弯路。本文还有配套的精品资源点击获取