Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

本页目录

插件 ABI 与 Document/Command 架构 ​

1. Qt 插件不是天然稳定 ABI ​

纯虚 C++ 接口 + Q_DECLARE_INTERFACE 只提供接口发现方式,不自动稳定:

  • Qt major/minor 和构建配置;
  • 编译器 C++ ABI、CRT、异常/RTTI;
  • Qt/标准库容器布局;
  • 第三方依赖;
  • 对象由哪侧分配/释放;
  • IID 与元数据版本。

完整插件 ABI、发现、隔离和卸载规则以插件工程知识区为准。本篇只保留 Qt CAE 中的角色和文档架构。

2. 插件角色 ​

Importer、Exporter、Mesher、Solver、PostProcessor 等角色可注册 command/action/service,不应直接掌控主窗口内部对象。Host 提供窄 context/service 接口,插件返回领域结果或异步 job。

不可信插件不能通过进程内 QPluginLoader 获得安全隔离;应移到受限进程,用 RPC/文件协议通信。

3. Document 是一致性边界 ​

text
Document
  +-- domain model / stable IDs
  +-- revision / dirty state
  +-- selection/view-independent state
  +-- undo stack
  +-- running jobs linked to revision
1
2
3
4
5
6

View 不拥有核心模型,插件也不保存易失裸指针。跨异步任务传 document ID + revision + immutable snapshot;结果回来先验证 document 仍存在且 revision 匹配。

4. Command 与 Undo ​

Command 封装一次可验证的领域变化:输入、前置条件、redo、undo 和显示文本。QUndoStack 适合 GUI 操作历史,但命令必须保证:

  • undo 恢复所有受影响不变量;
  • 大数据不无界复制,可用增量/共享快照;
  • mergeWith 只合并语义连续操作;
  • 外部副作用(文件/求解器)不能假装可简单 undo;
  • 异步命令完成和文档关闭有协议。

5. Action 注册 ​

插件注册 command descriptor(ID、文本、权限/上下文、handler),host 创建 QAction 并根据 selection/document state 更新 enabled。业务逻辑不要写死在 QAction slot,便于脚本、快捷键和测试复用。

ID 全局命名空间化,卸载插件前先注销 action/shortcut/menu,再确认没有 queued callback 和运行 job。

6. 保存与恢复 ​

Document 保存用明确 schema/version、原子文件替换和迁移;undo history 是否持久化需单独设计,不能序列化 QObject/Command 指针。自动恢复文件与正式保存分开,启动时提示并验证完整性。

7. 依赖方向 ​

text
UI/Qt adapters -> application services -> domain model
plugins --------> stable host ports ------^
infrastructure -> implements storage/solver ports
1
2
3

领域模型不反向依赖 QWidget/QPluginLoader,便于 headless 测试和进程外执行。

8. 面试速答 ​

为什么纯虚接口不等于稳定 ABI? C++ 布局、vtable、编译器/Qt/CRT 和异常仍是二进制契约。

异步求解结果如何避免覆盖新文档? 绑定 document ID/revision,提交时重验并走 command/transaction。

插件 QAction 为什么不直接捕获内部指针? 卸载/文档关闭后会悬空,且耦合 host 实现。

9. 自测答案 ​

Qt 插件能否安全热卸载?只有所有对象、连接、线程、事件、函数指针和第三方资源都已退出,实际很难,生产常选择加载后不卸载。Undo 能否撤销已提交远程求解?通常要设计补偿/新命令,不能简单恢复本地内存。

插件边界先定义生存期,再定义功能 ​

text
Host owns ModuleHandle
  └── owns PluginInstance(s)
      └── owns callbacks/resources/doc adapters

shutdown must reverse:
callbacks/tasks stop -> instances destroy -> module unload
1
2
3
4
5
6

如果宿主先卸载库,再析构插件对象或调用 deleter/vtable,就会跳到已卸载代码。插件引用计数必须覆盖对象、回调、后台任务和 GPU/Qt 资源。

Qt 插件元数据与 C++ ABI ​

Q_PLUGIN_METADATA/Q_DECLARE_INTERFACE 帮助发现与转换接口,不消除 C++ ABI 依赖。宿主和插件仍需兼容 Qt 主版本/构建配置、编译器 ABI、CRT、接口类布局和元对象系统。

text
metadata IID/version -> discovery compatibility
C++ interface/vtable -> binary compatibility
ownership functions  -> allocator compatibility
1
2
3

大型生态可在最外层使用版本化 C ABI 工厂/不透明 handle,再在同一工具链内适配为 Qt 对象。

接口版本化 ​

不要直接修改 v1 接口虚函数顺序。可定义新 IID/interface,插件按 query 提供支持版本:

text
Host asks IImporterV2
 -> plugin supports: return pointer
 -> otherwise fallback IImporterV1 or reject
1
2
3

能力协商比仅比较一个整数更适合可选特性。

跨边界类型与错误 ​

不受控 ABI 下避免传 STL/Qt 容器、异常和由对侧 delete 的对象。使用固定宽度 POD view、回调写入 host buffer、opaque handle 与 destroy。若边界受控,也应记录谁拥有 QString/QImage/mesh 数据以及调用线程。

Document 是应用组合根 ​

text
Document
├── domain entities / units / metadata
├── undo stack and dirty state
├── persistence adapter
├── compute task group
├── model/view adapters
└── render scene/resources
1
2
3
4
5
6
7

Widget 只发命令和显示状态,不直接修改多个领域容器。Document 对一次修改建立事务:校验、执行 command、更新 dirty/revision、发出窄通知。

Undo/Redo 的命令契约 ​

命令应保存足够的前后状态或可逆增量,且只作用于匹配 document revision:

text
prepare command -> redo -> revision++
undo -> restore invariant -> revision++
1
2

大型网格不能无脑复制,可保存结构化 delta、引用不可变 chunk 或 checkpoint;内存预算超限时合并/截断历史要告知用户。

异步结果的 revision 提交 ​

text
task starts on document revision 12
user edits -> revision 13
task finishes result(revision 12)
 -> reject, rebase or present as separate result
1
2
3
4

只靠 QObject 指针仍活着不够,结果可能语义过期。任务携带 document ID、revision 和参数 hash,提交点验证。

插件与 Document 的隔离 ​

插件不应长期持有 Document 裸指针。宿主提供窄 service/capability、snapshot 和受控 command 提交;卸载时撤销 capability,等待插件任务,断开连接,再释放实例。

连续追问与自测 ​

问:Qt 插件接口是否天然 ABI 稳定? 答:不天然,仍受 Qt/编译器/接口 vtable/CRT 等 ABI 约束。

问:为什么异步结果要带 revision? 答:对象仍存在不代表输入状态未改变,可防止旧计算覆盖新文档。

  1. 模块何时可卸载? 答:所有插件对象、回调、任务和资源均已销毁/停止后。
  2. 新增虚函数为何危险? 答:可能改变 vtable 槽位,使旧二进制按旧位置调用错误函数。
  3. Document dirty 何时清除? 答:对应 revision 成功持久化并确认后,而非开始保存时。

工程深化:把本篇知识落到项目里 ​

这一节不是为了凑篇幅,而是把《插件 ABI 与 Document/Command 架构》从“知道概念”推进到“能在项目里稳定使用”。 阅读时可以把每个知识点都追问成四件事:它保护什么边界,失败时有什么现象,怎样最小复现,怎样写进团队流程。

text
concept -> boundary -> failure signal -> minimal proof -> project rule
1

工程切片 1:最小可复现样例 ​

**场景。**围绕 事件循环 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。

**边界。**先判断这里讨论的是 事件循环、QObject 还是 模型数据 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 事件循环 -> QObject -> 模型数据 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 2:接口边界 ​

**场景。**围绕 QObject 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。

**边界。**先判断这里讨论的是 QObject、模型数据 还是 线程边界 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> QObject -> 模型数据 -> 线程边界 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 3:失败注入 ​

**场景。**围绕 模型数据 建一个很小的案例,不要一开始就放进完整业务系统。故意制造一个常见错误,让日志、断言或测试先失败,再用修复后的证据说明规则生效。

**边界。**先判断这里讨论的是 模型数据、线程边界 还是 资源生命周期 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 模型数据 -> 线程边界 -> 资源生命周期 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 4:跨平台差异 ​

**场景。**围绕 线程边界 建一个很小的案例,不要一开始就放进完整业务系统。至少比较 Windows、Linux 或不同编译器下的一个差异,记录差异属于标准、实现还是平台约定。

**边界。**先判断这里讨论的是 线程边界、资源生命周期 还是 诊断工具 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 线程边界 -> 资源生命周期 -> 诊断工具 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 5:性能观察 ​

**场景。**围绕 资源生命周期 建一个很小的案例,不要一开始就放进完整业务系统。用小规模和压力规模各跑一次,区分算法成本、同步成本、I/O 成本和工具链配置成本。

**边界。**先判断这里讨论的是 资源生命周期、诊断工具 还是 事件循环 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 资源生命周期 -> 诊断工具 -> 事件循环 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 6:生命周期 ​

**场景。**围绕 诊断工具 建一个很小的案例,不要一开始就放进完整业务系统。标出资源创建、转移、共享、停止和销毁的顺序,特别关注错误返回和提前退出路径。

**边界。**先判断这里讨论的是 诊断工具、事件循环 还是 QObject 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 诊断工具 -> 事件循环 -> QObject -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 7:诊断证据 ​

**场景。**围绕 事件循环 建一个很小的案例,不要一开始就放进完整业务系统。保留命令、日志、符号、栈、测试输出或截图,让结论可以被另一个环境重新验证。

**边界。**先判断这里讨论的是 事件循环、QObject 还是 模型数据 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 事件循环 -> QObject -> 模型数据 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 8:维护策略 ​

**场景。**围绕 QObject 建一个很小的案例,不要一开始就放进完整业务系统。把一次性经验沉淀到 README、CI、脚本、示例工程或检查表里,避免只存在个人记忆中。

**边界。**先判断这里讨论的是 QObject、模型数据 还是 线程边界 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> QObject -> 模型数据 -> 线程边界 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 9:版本演进 ​

**场景。**围绕 模型数据 建一个很小的案例,不要一开始就放进完整业务系统。为未来变更预留兼容策略:版本号、特性位、弃用窗口、迁移脚本和回滚入口。

**边界。**先判断这里讨论的是 模型数据、线程边界 还是 资源生命周期 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 模型数据 -> 线程边界 -> 资源生命周期 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 10:最小消费者 ​

**场景。**围绕 线程边界 建一个很小的案例,不要一开始就放进完整业务系统。从使用者角度写一个小程序或小工程,只依赖公开接口,验证安装包、头文件和运行时行为。

**边界。**先判断这里讨论的是 线程边界、资源生命周期 还是 诊断工具 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 线程边界 -> 资源生命周期 -> 诊断工具 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 11:边界复盘 ​

**场景。**围绕 资源生命周期 建一个很小的案例,不要一开始就放进完整业务系统。用一张表说明问题发生在源码、编译、链接、加载、运行、关闭或发布中的哪一层。

**边界。**先判断这里讨论的是 资源生命周期、诊断工具 还是 事件循环 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 资源生命周期 -> 诊断工具 -> 事件循环 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 12:反例训练 ​

**场景。**围绕 诊断工具 建一个很小的案例,不要一开始就放进完整业务系统。写出一个看似能工作但不可维护的方案,再说明它在规模、团队协作或发布时会怎样失败。

**边界。**先判断这里讨论的是 诊断工具、事件循环 还是 QObject 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 诊断工具 -> 事件循环 -> QObject -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 13:最小可复现样例 ​

**场景。**围绕 事件循环 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。

**边界。**先判断这里讨论的是 事件循环、QObject 还是 模型数据 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 事件循环 -> QObject -> 模型数据 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 14:接口边界 ​

**场景。**围绕 QObject 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。

**边界。**先判断这里讨论的是 QObject、模型数据 还是 线程边界 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> QObject -> 模型数据 -> 线程边界 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 15:失败注入 ​

**场景。**围绕 模型数据 建一个很小的案例,不要一开始就放进完整业务系统。故意制造一个常见错误,让日志、断言或测试先失败,再用修复后的证据说明规则生效。

**边界。**先判断这里讨论的是 模型数据、线程边界 还是 资源生命周期 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 模型数据 -> 线程边界 -> 资源生命周期 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 16:跨平台差异 ​

**场景。**围绕 线程边界 建一个很小的案例,不要一开始就放进完整业务系统。至少比较 Windows、Linux 或不同编译器下的一个差异,记录差异属于标准、实现还是平台约定。

**边界。**先判断这里讨论的是 线程边界、资源生命周期 还是 诊断工具 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 线程边界 -> 资源生命周期 -> 诊断工具 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 17:性能观察 ​

**场景。**围绕 资源生命周期 建一个很小的案例,不要一开始就放进完整业务系统。用小规模和压力规模各跑一次,区分算法成本、同步成本、I/O 成本和工具链配置成本。

**边界。**先判断这里讨论的是 资源生命周期、诊断工具 还是 事件循环 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 资源生命周期 -> 诊断工具 -> 事件循环 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 18:生命周期 ​

**场景。**围绕 诊断工具 建一个很小的案例,不要一开始就放进完整业务系统。标出资源创建、转移、共享、停止和销毁的顺序,特别关注错误返回和提前退出路径。

**边界。**先判断这里讨论的是 诊断工具、事件循环 还是 QObject 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 诊断工具 -> 事件循环 -> QObject -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 19:诊断证据 ​

**场景。**围绕 事件循环 建一个很小的案例,不要一开始就放进完整业务系统。保留命令、日志、符号、栈、测试输出或截图,让结论可以被另一个环境重新验证。

**边界。**先判断这里讨论的是 事件循环、QObject 还是 模型数据 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 事件循环 -> QObject -> 模型数据 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 20:维护策略 ​

**场景。**围绕 QObject 建一个很小的案例,不要一开始就放进完整业务系统。把一次性经验沉淀到 README、CI、脚本、示例工程或检查表里,避免只存在个人记忆中。

**边界。**先判断这里讨论的是 QObject、模型数据 还是 线程边界 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> QObject -> 模型数据 -> 线程边界 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 21:版本演进 ​

**场景。**围绕 模型数据 建一个很小的案例,不要一开始就放进完整业务系统。为未来变更预留兼容策略:版本号、特性位、弃用窗口、迁移脚本和回滚入口。

**边界。**先判断这里讨论的是 模型数据、线程边界 还是 资源生命周期 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 模型数据 -> 线程边界 -> 资源生命周期 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 22:最小消费者 ​

**场景。**围绕 线程边界 建一个很小的案例,不要一开始就放进完整业务系统。从使用者角度写一个小程序或小工程,只依赖公开接口,验证安装包、头文件和运行时行为。

**边界。**先判断这里讨论的是 线程边界、资源生命周期 还是 诊断工具 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 线程边界 -> 资源生命周期 -> 诊断工具 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 23:边界复盘 ​

**场景。**围绕 资源生命周期 建一个很小的案例,不要一开始就放进完整业务系统。用一张表说明问题发生在源码、编译、链接、加载、运行、关闭或发布中的哪一层。

**边界。**先判断这里讨论的是 资源生命周期、诊断工具 还是 事件循环 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 资源生命周期 -> 诊断工具 -> 事件循环 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

本篇收束 ​

掌握《插件 ABI 与 Document/Command 架构》的标志,不是记住所有名词,而是能把 事件循环、QObject、模型数据、线程边界、资源生命周期、诊断工具 放进一条可验证的工程链路。 先用最小样例建立判断,再用测试和脚本固定判断,最后把失败证据留给未来的自己和团队。

最后更新于:

Pager
下一篇← C++ 编程 / C++ Programming

持续记录,持续成长

Copyright © Tidenflow