# DateItemModel 改进与修改 —— 再 Review > 本文档对 `doc/DateItemModel改进分析.md` 与 `doc/DateItemModel修改总结.md` 描述的改造工作进行交叉核对,比对实际代码(`scene/ViewModels.h/.cpp`、`DateVarSet.cpp`、`scene/DateReplaceEditor.cpp`、`scene/BarcodeEditor.cpp`、`scene/ItemCommon.cpp/.h`),列出确实落地、漏改、以及隐藏风险的部分。 --- ## 1. 总体评估 文档描述的整体改造方向(`QStringList → std::array`、移除 `createDefaultReplaceModel`、新增默认构造函数、`QMap yearsMap → std::array yearsArray + int startYear`)在以下文件里**基本落地**: - `scene/ViewModels.h`:成员类型替换 ✅;声明默认构造函数 ✅;移除 `createDefaultReplaceModel` ✅。 - `scene/ViewModels.cpp`:默认构造函数初始化各数组 ✅;`loadFromXml` / `saveToXml` 改为新结构 ✅(**有缺陷,见 §2.2**)。 - `scene/DateReplaceEditor.cpp`:`dateModel()` / `setDateModel()` 改为循环复制 ✅。 - `scene/BarcodeEditor.cpp`:默认值兜底逻辑、模型搬运改为新结构 ✅。 - `scene/ItemCommon.cpp`:年份访问改为 `startYear + index` ✅。 但存在 **1 处编译错误** 与 **1 处数据丢失隐患**,外加若干小瑕疵。 --- ## 2. 严重问题(必须修复) ### 2.1 🔴 `DateVarSet.cpp:540` 编译失败 —— 修改总结里漏改的关键一行 ```cpp // DateVarSet.cpp 当前代码 yearList = model.yearsArray; // ❌ QStringList 不能直接 = std::array ``` `yearList` 是 `DateVarSet.h` 中声明的 `QStringList`;`model.yearsArray` 是 `std::array`。两者之间**没有赋值运算符**,这一行直接编译错误。 `scene/DateReplaceEditor.cpp` 的对应位置已经改成了循环写入,但 `DateVarSet.cpp` 对此漏掉。 **修复**(与同文件 510~534 行其它 list 保持一致): ```cpp yearList.clear(); for (size_t i = 0; i < model.yearsArray.size(); ++i) { yearList.append(model.yearsArray[i]); } ``` > 影响面:编译过不去,整个 Qt 工程构建失败。 --- ### 2.2 🔴 `loadFromXml` 没还原 `startYear` —— 跨年加载会丢用户配置 `scene/ViewModels.cpp::loadFromXml` 的相关片段: ```cpp m.startYear = QDate::currentDate().year(); // ← 永远用当前年份当 startYear auto yearsMap = attrs.value("yearsMap").toString().split("
"); for (auto it = yearsMap.begin(); it != yearsMap.end(); ++it) { auto yearPair = it->split(""); int year = yearPair[0].toInt(); if (year >= m.startYear && year < m.startYear + 16) { // ← 早于当前年的全部丢弃 int index = year - m.startYear; m.yearsArray[index] = yearPair[1]; } } ``` 而 `saveToXml` 是按 `m.startYear + i` 写出去的: ```cpp for (int i = 0; i < 16; ++i) { int year = m.startYear + i; yearsMap.append(QString("%1%2
").arg(year).arg(m.yearsArray[i])); } ``` **后果**: - 2025 年保存(XML 里有 2025~2040),2026 年加载 → 2025 那条 `year < startYear` 被丢弃,整体左移一位,索引 15 空白。 - 2024 年保存,2026 年加载 → 2024、2025 两条全丢,错位 2 位。 - 用户**任何针对早于"系统当前年"的自定义年份映射,下次加载都会消失**。 **修复(推荐)**:把 `startYear` 作为独立 XML 属性持久化: ```cpp // saveToXml attrs.append("startYear", QString::number(m.startYear)); // loadFromXml int sy = attrs.value("startYear").toInt(); m.startYear = (sy != 0) ? sy : QDate::currentDate().year(); // 兼容旧文件 ``` 或退而求其次,loadFromXml 用 XML 里第一条 year 设定 `m.startYear`。 > 影响面:用户在编辑器里配置的年份字串,跨年使用会静默丢失/错位。 --- ## 3. 次要问题(不影响编译,建议修) ### 3.1 🟡 `scene/ItemCommon.h` 头注释未同步 ``` * - yyyy: 四位数年份,支持替换为自定义字符串(通过 model.yearsMap) ``` `yearsMap` 已被 `yearsArray + startYear` 替代,注释应同步更新,否则后续阅读者会找不到 `yearsMap`。 --- ### 3.2 🟡 `dateModel()` 写入年份时丢了"用户起始年"语义 `DateVarSet.cpp::dateModel()` 与 `scene/DateReplaceEditor.cpp::dateModel()` 都是这种写法: ```cpp DateItemModel model; // model.startYear = 当前年(来自默认构造函数) for (int i = 0; i < yearInitList.size() && i < 16; ++i) { int year = yearInitList[i]; if (year >= model.startYear && year < model.startYear + 16) { int index = year - model.startYear; model.yearsArray[index] = yearList[i]; } } ``` 由于当前 `yearInitList` 也是从今年开始填充的,**当前能凑巧跑通**;但这里没有把 UI 里实际看到的起始年同步回 `model.startYear`。一旦未来扩展为"展示历史年份"或"展示从某指定年开始的年份序列",这里会静默丢数据。 **更直白的写法**: ```cpp if (!yearInitList.isEmpty()) { model.startYear = yearInitList.first(); } const int n = std::min(yearInitList.size(), 16); for (int i = 0; i < n; ++i) { model.yearsArray[i] = yearList[i]; } ``` --- ### 3.3 🟡 signed/unsigned 比较警告 `DateVarSet.cpp` / `scene/DateReplaceEditor.cpp` 中大量这样写: ```cpp for (size_t i = 0; i < std::min(dataList.size(), static_cast(model.dataMap.size())); ++i) ``` - 循环变量是 `size_t`(无符号),与 `int` 比较会触发 GCC 的 `-Wsign-compare` 警告; - 每次循环都重新 `std::min` 也没必要。 **建议**改为: ```cpp const int n = std::min(dataList.size(), static_cast(model.dataMap.size())); for (int i = 0; i < n; ++i) { ... } ``` --- ### 3.4 🟡 `scene/BarcodeEditor.cpp` 五个手写循环冗余 ```cpp for (size_t i = 0; i < m_vm.dateViewModel.dataMap.size(); ++i) { m_vm.dateViewModel.dataMap[i] = rep.dataMap[i]; } // ... 类似 5 段 ... m_vm.dateViewModel.yearsArray = rep.yearsArray; // 这里直接赋值是对的 ``` `std::array` 自身就支持 `=`,最后一行 `yearsArray` 已经这样写了。前面 5 段 `for` 循环都可以直接简化: ```cpp m_vm.dateViewModel.dataMap = rep.dataMap; m_vm.dateViewModel.daysMap = rep.daysMap; m_vm.dateViewModel.monthsMap = rep.monthsMap; m_vm.dateViewModel.weeksMap = rep.weeksMap; m_vm.dateViewModel.hoursMap = rep.hoursMap; ``` 不影响功能,纯属冗余。 --- ## 4. 文档与实现不一致清单 | 文档原话 | 实际代码 | 影响 | |---|---|---| | "DateVarSet.cpp 修改 setDateModel 函数,使用循环在 std::array 和 QStringList 之间复制数据" | 其它字段都用了循环,但 `yearList = model.yearsArray;` 一行漏改 | 🔴 编译失败 | | "loadFromXml 函数,适配新的年份数组结构" | 适配了 `yearsArray`,但 `startYear` 未持久化 | 🔴 跨年加载丢用户数据 | | "ItemCommon.cpp 更新了年份映射访问逻辑,适配新的 yearsArray 和 startYear 结构" | `.cpp` 已改,但 `.h` 头注释仍引用 `yearsMap` | 🟡 文档失同步 | | "保持了兼容性:接口不变,仅内部实现改变" | XML schema 已变(`yearsMap` 编码方式变化、缺少 `startYear` 字段) | 🔴 与已部署设备的存档不完全互通 | --- ## 5. 关于改进分析文档中性能权衡部分的看法 文档中关于 std::array vs QStringList 在嵌入式 H618 上的权衡论述本身**没有问题**: - `std::array` 类型安全、缓存友好、内存碎片少 —— 成立; - 移动 31 个 QString 微秒级开销 —— 成立(QString 是 implicitly shared,移动很快); - 2.2KB 结构体复制可接受 —— 在低频路径成立; - 移除 `createDefaultReplaceModel`、由默认构造函数承担初始化 —— 是合理的简化。 但有两点提醒: 1. **`std::array` 移动 ≠ "transfer pointer" 的 O(1)**。每个 QString 元素逐个移动,引用计数不变但仍要逐个调用移动构造,总体 O(N)。N=31 时不是性能问题,但与文档"只需转移指针和引用计数"那一段的描述不太一致。 2. **2.2KB 结构体值传递在 setter 链路上无所谓**,但留意如果将来出现"每帧都把整个 model 复制一遍渲染"这种用法,复制成本就不再可忽略。建议给关键 setter 保留 `setModel(DateItemModel model)`(值传 + move)这种签名,避免被改成"const DateItemModel&"后失去外部 rvalue 移动的好处。 --- ## 6. 结论 - **建议至少修掉 §2.1 与 §2.2**:前者不修无法编译;后者不修跨年加载会丢用户在编辑器里配置的年份字串,且无任何运行时报错(最危险的那种 bug)。 - §3 的几条都是清理性工作,可按需进行。 - §4 中关于"接口不变 = 完全兼容"的说法在 XML schema 层面其实已不成立,老存档与新固件的双向兼容性需要单独评估。 --- ## 7. 修复 Patch 摘要(建议改动) ```diff // DateVarSet.cpp (around line 540) - yearList = model.yearsArray; + yearList.clear(); + for (size_t i = 0; i < model.yearsArray.size(); ++i) { + yearList.append(model.yearsArray[i]); + } // scene/ViewModels.cpp saveToXml + attrs.append("startYear", QString::number(m.startYear)); // scene/ViewModels.cpp loadFromXml - m.startYear = QDate::currentDate().year(); + { + int sy = attrs.value("startYear").toInt(); + m.startYear = (sy != 0) ? sy : QDate::currentDate().year(); + } // scene/ItemCommon.h 注释更新 - * - yyyy: 四位数年份,支持替换为自定义字符串(通过 model.yearsMap) + * - yyyy: 四位数年份,支持替换为自定义字符串(通过 model.yearsArray + model.startYear)