Qt 数据、I/O 与序列化兼容
1. 数据类型选择
- UI/Qt API 文本:
QString; - 协议/文件原始字节:
QByteArray; - 长期 UTF-8 边界:显式
toUtf8/fromUtf8; - 大型数值数组:连续标准/领域容器,避免每个值包装 QVariant;
- QVariant:封闭属性系统和元对象交互,不是任意序列化格式。
2. QSaveFile
重要文档保存优先 QSaveFile:写临时文件,成功 commit() 原子替换目标(具体保证受文件系统影响)。
QSaveFile file(path);
if (!file.open(QIODevice::WriteOnly)) return error(file.errorString());
if (file.write(bytes) != bytes.size()) return error(file.errorString());
if (!file.commit()) return error(file.errorString());仍需处理磁盘满、权限、目录持久化和旧文件恢复;不要忽略 write/commit 返回值。
3. QDataStream 版本
QDataStream stream(&device);
stream.setVersion(QDataStream::Qt_6_5);
stream.setByteOrder(QDataStream::LittleEndian);
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);固定 stream 版本稳定 Qt 内建类型的某套编码规则,不会自动为自定义文档提供 schema 演进。文件头还应包含 magic、应用格式版本、feature flags、长度/校验和。
读取不可信长度前设置上限和剩余字节检查,操作后检查 stream.status();不要先按攻击者长度分配数 GB。
4. 长期格式
长期/跨语言数据可评估 JSON/CBOR、Protocol Buffers、HDF5、VTK 等;选择取决于 schema、随机访问、精度、压缩和生态。即使使用成熟格式,领域层仍需版本迁移和未知字段策略。
二进制浮点、字节序、整数宽度明确固定;不要直接 dump C++ struct(padding/ABI)。
5. QSettings
QSettings 后端和存储位置跨平台不同,适合用户偏好,不适合大型工程文档或安全 secret。key 要命名空间化并提供默认值/迁移。同步/写入失败需检查 status。
多个进程同时写同一配置的合并语义有限;复杂共享配置使用明确事务存储。
6. QProcess
start -> wait for started/event -> read stdout/stderr incrementally
-> finished(exitCode, status) / errorOccurred使用 program + argument list,不拼 shell 命令;设置工作目录和环境白名单;分别排空 stdout/stderr 防子进程因 pipe 满阻塞。限制输出大小并解析增量行/帧。
关闭:先请求求解器协议取消/terminate,deadline 后 kill,随后 wait 并收集退出状态。terminate() 的平台语义不同。
7. 文本解析
QTextStream 编码和 locale 要显式;科学数值格式常要求 C locale 和稳定精度。正则适合局部字段,不适合复杂嵌套语法。解析报告包含行/列、上下文和可恢复/致命分类。
8. 面试速答
固定 QDataStream version 是否得到永久兼容? 不会,只固定 Qt 类型规则,自定义 schema 仍要版本化。
QSaveFile 是否等于数据库事务? 不等于,它主要改善单文件替换;跨多文件/外部副作用需更高层事务。
QProcess 为什么要同时读取 stderr? 子进程 pipe 有界,不读可能阻塞子进程,且错误诊断会丢失。
9. 自测答案
直接序列化 QVector<MyClass> 前要确认什么?MyClass 的 stream operator、格式版本、长度上限、字节序和兼容策略。QSettings 能否存密码?不应默认视为安全 secret store。
文件格式需要双层版本
container/stream encoding version (Qt serialization behavior)
application schema version (business fields and meaning)文件头至少包含 magic、schema version、feature flags、字节序/stream version 和长度;读取先验证上限,再分配。
[magic][schema][flags][section count]
-> each section [type][length][checksum?][payload]QDataStream 的边界
双方设置相同 QDataStream::Version 和 byte order;状态错误后停止继续读,不能让后续字段建立在错位 offset 上。Qt 类型编码不自动提供恶意输入安全,字符串/容器计数仍要受文件总预算约束。
JSON 与二进制选择
JSON 可读、跨语言、字段演化方便,但数值精度、大小和解析成本需注意;二进制紧凑快速,却必须严谨版本化。大数组/网格可用分块二进制,元数据用 JSON/CBOR,形成混合 container。
原子保存
write temporary in same filesystem
-> flush/validate
-> atomically replace target where platform supports
-> fsync file/directory if durability requiredQSaveFile 封装常见临时写+commit,但持久性和权限/元数据行为仍需按平台验证。保存失败不能清除 document dirty flag。
增量与分块读取
大 CAE 文件不应一次分配全部:
read header/index -> validate offsets
-> demand-load mesh/result chunks
-> cache with memory budget
-> cancel on document close所有 offset+length 运算防溢出,区段不得重叠/越界;压缩数据还限制解压后大小和 CPU 时间。
文本编码
网络/文件统一明确 UTF-8;读取用 QString::fromUtf8 并决定非法序列策略。不要用 local8Bit 作为永久格式。路径是平台对象,显示文本与原始文件系统编码也不要随意互转丢失。
Schema 演化
reader supports vN-k ... vN
old fields -> defaults/migration
unknown optional sections -> skip by length
unknown required feature -> reject clearly迁移应转换到当前内存模型,保存时通常写当前版本;保留测试 corpus 覆盖历史文件和损坏输入。
异步 I/O 生命周期
后台读取结果回到 GUI 前,document 可能已关闭/切换。任务携带 document ID/generation,完成时核对仍为当前对象;取消后仍可能收到在途 completion,不能仅断开 signal。
连续追问与自测
问:QDataStream version 是否等于文件版本? 答:不是,它控制 Qt 编码;业务字段语义需独立 schema。
问:为什么临时文件要同文件系统? 答:跨文件系统 rename 通常不具备同样原子替换语义。
- 读取 length 后第一步? 答:检查上限、剩余文件范围和整数溢出。
- 保存失败 dirty flag 怎么办? 答:保持脏状态并向用户报告。
- 异步结果如何防止写入新文档? 答:携带稳定 document identity/generation 并在提交时验证。
工程深化:把本篇知识落到项目里
这一节不是为了凑篇幅,而是把《Qt 数据、I/O 与序列化兼容》从“知道概念”推进到“能在项目里稳定使用”。 阅读时可以把每个知识点都追问成四件事:它保护什么边界,失败时有什么现象,怎样最小复现,怎样写进团队流程。
concept -> boundary -> failure signal -> minimal proof -> project rule工程切片 1:最小可复现样例
**场景。**围绕 QDataStream 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。
**边界。**先判断这里讨论的是 QDataStream、schema version 还是 QSaveFile 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QDataStream -> schema version -> QSaveFile -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 2:接口边界
**场景。**围绕 schema version 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。
**边界。**先判断这里讨论的是 schema version、QSaveFile 还是 QProcess 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> schema version -> QSaveFile -> QProcess -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 3:失败注入
**场景。**围绕 QSaveFile 建一个很小的案例,不要一开始就放进完整业务系统。故意制造一个常见错误,让日志、断言或测试先失败,再用修复后的证据说明规则生效。
**边界。**先判断这里讨论的是 QSaveFile、QProcess 还是 编码 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QSaveFile -> QProcess -> 编码 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 4:跨平台差异
**场景。**围绕 QProcess 建一个很小的案例,不要一开始就放进完整业务系统。至少比较 Windows、Linux 或不同编译器下的一个差异,记录差异属于标准、实现还是平台约定。
**边界。**先判断这里讨论的是 QProcess、编码 还是 损坏输入 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QProcess -> 编码 -> 损坏输入 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 5:性能观察
**场景。**围绕 编码 建一个很小的案例,不要一开始就放进完整业务系统。用小规模和压力规模各跑一次,区分算法成本、同步成本、I/O 成本和工具链配置成本。
**边界。**先判断这里讨论的是 编码、损坏输入 还是 QDataStream 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 编码 -> 损坏输入 -> QDataStream -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 6:生命周期
**场景。**围绕 损坏输入 建一个很小的案例,不要一开始就放进完整业务系统。标出资源创建、转移、共享、停止和销毁的顺序,特别关注错误返回和提前退出路径。
**边界。**先判断这里讨论的是 损坏输入、QDataStream 还是 schema version 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 损坏输入 -> QDataStream -> schema version -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 7:诊断证据
**场景。**围绕 QDataStream 建一个很小的案例,不要一开始就放进完整业务系统。保留命令、日志、符号、栈、测试输出或截图,让结论可以被另一个环境重新验证。
**边界。**先判断这里讨论的是 QDataStream、schema version 还是 QSaveFile 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QDataStream -> schema version -> QSaveFile -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 8:维护策略
**场景。**围绕 schema version 建一个很小的案例,不要一开始就放进完整业务系统。把一次性经验沉淀到 README、CI、脚本、示例工程或检查表里,避免只存在个人记忆中。
**边界。**先判断这里讨论的是 schema version、QSaveFile 还是 QProcess 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> schema version -> QSaveFile -> QProcess -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 9:版本演进
**场景。**围绕 QSaveFile 建一个很小的案例,不要一开始就放进完整业务系统。为未来变更预留兼容策略:版本号、特性位、弃用窗口、迁移脚本和回滚入口。
**边界。**先判断这里讨论的是 QSaveFile、QProcess 还是 编码 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QSaveFile -> QProcess -> 编码 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 10:最小消费者
**场景。**围绕 QProcess 建一个很小的案例,不要一开始就放进完整业务系统。从使用者角度写一个小程序或小工程,只依赖公开接口,验证安装包、头文件和运行时行为。
**边界。**先判断这里讨论的是 QProcess、编码 还是 损坏输入 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QProcess -> 编码 -> 损坏输入 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 11:边界复盘
**场景。**围绕 编码 建一个很小的案例,不要一开始就放进完整业务系统。用一张表说明问题发生在源码、编译、链接、加载、运行、关闭或发布中的哪一层。
**边界。**先判断这里讨论的是 编码、损坏输入 还是 QDataStream 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 编码 -> 损坏输入 -> QDataStream -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 12:反例训练
**场景。**围绕 损坏输入 建一个很小的案例,不要一开始就放进完整业务系统。写出一个看似能工作但不可维护的方案,再说明它在规模、团队协作或发布时会怎样失败。
**边界。**先判断这里讨论的是 损坏输入、QDataStream 还是 schema version 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 损坏输入 -> QDataStream -> schema version -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 13:最小可复现样例
**场景。**围绕 QDataStream 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。
**边界。**先判断这里讨论的是 QDataStream、schema version 还是 QSaveFile 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QDataStream -> schema version -> QSaveFile -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 14:接口边界
**场景。**围绕 schema version 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。
**边界。**先判断这里讨论的是 schema version、QSaveFile 还是 QProcess 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> schema version -> QSaveFile -> QProcess -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 15:失败注入
**场景。**围绕 QSaveFile 建一个很小的案例,不要一开始就放进完整业务系统。故意制造一个常见错误,让日志、断言或测试先失败,再用修复后的证据说明规则生效。
**边界。**先判断这里讨论的是 QSaveFile、QProcess 还是 编码 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QSaveFile -> QProcess -> 编码 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 16:跨平台差异
**场景。**围绕 QProcess 建一个很小的案例,不要一开始就放进完整业务系统。至少比较 Windows、Linux 或不同编译器下的一个差异,记录差异属于标准、实现还是平台约定。
**边界。**先判断这里讨论的是 QProcess、编码 还是 损坏输入 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QProcess -> 编码 -> 损坏输入 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 17:性能观察
**场景。**围绕 编码 建一个很小的案例,不要一开始就放进完整业务系统。用小规模和压力规模各跑一次,区分算法成本、同步成本、I/O 成本和工具链配置成本。
**边界。**先判断这里讨论的是 编码、损坏输入 还是 QDataStream 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 编码 -> 损坏输入 -> QDataStream -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 18:生命周期
**场景。**围绕 损坏输入 建一个很小的案例,不要一开始就放进完整业务系统。标出资源创建、转移、共享、停止和销毁的顺序,特别关注错误返回和提前退出路径。
**边界。**先判断这里讨论的是 损坏输入、QDataStream 还是 schema version 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 损坏输入 -> QDataStream -> schema version -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 19:诊断证据
**场景。**围绕 QDataStream 建一个很小的案例,不要一开始就放进完整业务系统。保留命令、日志、符号、栈、测试输出或截图,让结论可以被另一个环境重新验证。
**边界。**先判断这里讨论的是 QDataStream、schema version 还是 QSaveFile 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QDataStream -> schema version -> QSaveFile -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 20:维护策略
**场景。**围绕 schema version 建一个很小的案例,不要一开始就放进完整业务系统。把一次性经验沉淀到 README、CI、脚本、示例工程或检查表里,避免只存在个人记忆中。
**边界。**先判断这里讨论的是 schema version、QSaveFile 还是 QProcess 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> schema version -> QSaveFile -> QProcess -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 21:版本演进
**场景。**围绕 QSaveFile 建一个很小的案例,不要一开始就放进完整业务系统。为未来变更预留兼容策略:版本号、特性位、弃用窗口、迁移脚本和回滚入口。
**边界。**先判断这里讨论的是 QSaveFile、QProcess 还是 编码 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QSaveFile -> QProcess -> 编码 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 22:最小消费者
**场景。**围绕 QProcess 建一个很小的案例,不要一开始就放进完整业务系统。从使用者角度写一个小程序或小工程,只依赖公开接口,验证安装包、头文件和运行时行为。
**边界。**先判断这里讨论的是 QProcess、编码 还是 损坏输入 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> QProcess -> 编码 -> 损坏输入 -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 23:边界复盘
**场景。**围绕 编码 建一个很小的案例,不要一开始就放进完整业务系统。用一张表说明问题发生在源码、编译、链接、加载、运行、关闭或发布中的哪一层。
**边界。**先判断这里讨论的是 编码、损坏输入 还是 QDataStream 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 编码 -> 损坏输入 -> QDataStream -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
工程切片 24:反例训练
**场景。**围绕 损坏输入 建一个很小的案例,不要一开始就放进完整业务系统。写出一个看似能工作但不可维护的方案,再说明它在规模、团队协作或发布时会怎样失败。
**边界。**先判断这里讨论的是 损坏输入、QDataStream 还是 schema version 的责任;如果三者混在一起,先把输入、输出和所有权拆开。
input -> 损坏输入 -> QDataStream -> schema version -> observable result**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。
**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。
**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?
本篇收束
掌握《Qt 数据、I/O 与序列化兼容》的标志,不是记住所有名词,而是能把 QDataStream、schema version、QSaveFile、QProcess、编码、损坏输入 放进一条可验证的工程链路。 先用最小样例建立判断,再用测试和脚本固定判断,最后把失败证据留给未来的自己和团队。