Files
VX5_KRO_GearPump/doc/DateItemModel改进与修改_再review.md
T
2026-05-11 09:11:30 +08:00

9.7 KiB
Raw Blame History

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<QString,N>、移除 createDefaultReplaceModel、新增默认构造函数、QMap<int,QString> yearsMap → std::array<QString,16> 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 编译失败 —— 修改总结里漏改的关键一行

// DateVarSet.cpp 当前代码
yearList = model.yearsArray;   // ❌ QStringList 不能直接 = std::array<QString,16>

yearList 是 DateVarSet.h 中声明的 QStringList;model.yearsArray 是 std::array<QString, 16>。两者之间没有赋值运算符,这一行直接编译错误。

scene/DateReplaceEditor.cpp 的对应位置已经改成了循环写入,但 DateVarSet.cpp 对此漏掉。

修复(与同文件 510~534 行其它 list 保持一致):

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 的相关片段:

m.startYear = QDate::currentDate().year();   // ← 永远用当前年份当 startYear
auto yearsMap = attrs.value("yearsMap").toString().split("<br>");
for (auto it = yearsMap.begin(); it != yearsMap.end(); ++it) {
    auto yearPair = it->split("<subbr>");
    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 写出去的:

for (int i = 0; i < 16; ++i) {
    int year = m.startYear + i;
    yearsMap.append(QString("%1<subbr>%2<br>").arg(year).arg(m.yearsArray[i]));
}

后果:

  • 2025 年保存(XML 里有 2025~2040),2026 年加载 → 2025 那条 year < startYear 被丢弃,整体左移一位,索引 15 空白。
  • 2024 年保存,2026 年加载 → 2024、2025 两条全丢,错位 2 位。
  • 用户任何针对早于"系统当前年"的自定义年份映射,下次加载都会消失。

修复(推荐):把 startYear 作为独立 XML 属性持久化:

// 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() 都是这种写法:

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。一旦未来扩展为"展示历史年份"或"展示从某指定年开始的年份序列",这里会静默丢数据。

更直白的写法:

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 中大量这样写:

for (size_t i = 0; i < std::min(dataList.size(), static_cast<int>(model.dataMap.size())); ++i)
  • 循环变量是 size_t(无符号),与 int 比较会触发 GCC 的 -Wsign-compare 警告;
  • 每次循环都重新 std::min 也没必要。

建议改为:

const int n = std::min<int>(dataList.size(), static_cast<int>(model.dataMap.size()));
for (int i = 0; i < n; ++i) { ... }

3.4 🟡 scene/BarcodeEditor.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 循环都可以直接简化:

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<QString,N> 移动 ≠ "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 摘要(建议改动)

// 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)