【LibreCAD】LC_UndoSection 类详解

这两段代码是来自 LibreCADLC_UndoSection 类的头文件和实现文件。这是一个RAII(Resource Acquisition Is Initialization) 设计模式的典型应用,用于简化撤销/重做操作的错误处理。

1. 头文件部分 (lc_undosection.h)

类概述:

cpp 复制代码
/**
 * \brief This class is a wrapper for RS_Undo methods
 * 
 * 这是一个包装器类,确保 RS_Undo::startUndoCycle() 和 
 * RS_Undo::endUndoCycle() 的调用总是平衡的。
 * 构造函数调用 startUndoCycle()
 * 析构函数调用 endUndoCycle()
 * 还处理 RS_Document 指针的验证。
 */
class LC_UndoSection {

核心设计理念:

  • RAII模式:通过构造函数和析构函数自动管理资源
  • 异常安全:确保撤销周期的开始和结束总是配对的
  • 简化使用:特别是在 RS_Creation 和 RS_Modification 类中

类结构:

cpp 复制代码
class LC_UndoSection {
public:
    // 构造函数
    LC_UndoSection(RS_Document * doc, LC_GraphicViewport *view, bool handleUndo = true);
    
    // 析构函数
    ~LC_UndoSection();
    
    // 添加可撤销对象的方法
    void addUndoable(RS_Undoable * undoable);

private:
    RS_Document *document {nullptr};        // 文档指针
    LC_GraphicViewport* viewport {nullptr}; // 视口指针
    bool valid {true};                      // 有效性标志
};

2. 实现文件部分

构造函数实现:

cpp 复制代码
LC_UndoSection::LC_UndoSection(RS_Document *doc, LC_GraphicViewport* view, const bool handleUndo /*= true*/) :
    document( doc),
    viewport(view),
    valid( handleUndo && nullptr != doc && nullptr != view){
    if (valid) {
        document->startUndoCycle();
    }
}

构造函数逻辑:

  1. 初始化成员变量
  2. 检查有效性条件:
    • handleUndo 参数为 true
    • document 指针非空
    • viewport 指针非空
  3. 如果有效,开始撤销周期

析构函数实现:

cpp 复制代码
LC_UndoSection::~LC_UndoSection(){
    try {
        if (valid && viewport != nullptr) {
            RS_Undoable *relativeZeroUndoable = viewport->getRelativeZeroUndoable();
            if (relativeZeroUndoable != nullptr) {
                document->addUndoable(relativeZeroUndoable);
            }
            document->endUndoCycle();
        }
    } catch (...) {
    }
}

析构函数关键特性:

  1. 异常安全 :使用 try-catch(...) 捕获所有异常,防止异常传播
  2. 添加相对零点:获取视口的相对零点(可能是坐标系原点或参考点)
  3. 结束撤销周期:确保撤销周期正确结束
  4. 空指针检查:再次检查指针有效性

addUndoable 方法:

cpp 复制代码
void LC_UndoSection::addUndoable(RS_Undoable *undoable){
    if (valid) {
        document->addUndoable( undoable);
    }
}
  • 只在有效状态下添加可撤销对象
  • 直接委托给文档对象处理

设计模式解析

RAII(资源获取即初始化)模式:

cpp 复制代码
{
    LC_UndoSection undoSection(document, viewport);  // 构造函数开始撤销周期
    // 执行各种操作...
    undoSection.addUndoable(entity1);
    undoSection.addUndoable(entity2);
    // 作用域结束,析构函数自动结束撤销周期
}

异常安全保证:

进入作用域 创建 LC_UndoSection 开始撤销周期 执行操作
可能抛出异常 正常执行 抛出异常 离开作用域 调用析构函数 异常安全处理 结束撤销周期 资源正确释放

使用场景示例

创建操作中的使用:

cpp 复制代码
// RS_Creation 类中的典型用法
void RS_Creation::createLine() {
    LC_UndoSection undoSection(document, viewport);
    
    // 创建线条实体
    RS_Line* line = new RS_Line(...);
    
    // 添加到文档
    document->addEntity(line);
    
    // 添加到撤销管理器
    undoSection.addUndoable(line);
    
    // 作用域结束,自动结束撤销周期
}

设计优势

1. 自动资源管理

  • 无需手动调用 startUndoCycle()endUndoCycle()
  • 即使在异常情况下也能保证资源释放

2. 简化代码

  • 避免了复杂的错误处理代码
  • 减少了撤销管理相关的重复代码

3. 健壮性

  • 指针有效性检查
  • 异常安全处理
  • 状态验证机制

4. 可维护性

  • 清晰的职责分离
  • 易于调试和测试
  • 减少内存泄漏风险

与 RS_UndoCycle 的关系

LC_UndoSection高层封装 ,而 RS_UndoCycle底层数据结构

cpp 复制代码
// 关系示意
LC_UndoSection (RAII包装器)
    ↓
RS_Undo (撤销管理器)
    ↓
RS_UndoCycle (撤销周期容器)
    ↓
RS_Undoable (可撤销对象)

这种分层设计使得 LibreCAD 的撤销系统既灵活又易于使用。

相关推荐
从此不归路1 小时前
Qt5 进阶【7】网络请求与 REST API 实战:QNetworkAccessManager 深度应用
开发语言·c++·qt
Henry Zhu1232 小时前
Qt Model/View架构详解(三):自定义模型
开发语言·qt
老歌老听老掉牙2 小时前
16宫格属性分析系统:打造专业级科学数据可视化工具
c++·qt·可视化
qq_397562313 小时前
Qt_工程执行逻辑_窗口逻辑
开发语言·qt
SNAKEpc121383 小时前
PyQtGraph中的PlotWidget详解
python·qt·pyqt
m0_497214154 小时前
qt实现打印机功能
开发语言·qt
机器视觉知识推荐、就业指导5 小时前
用 Qt 做商业软件,会不会“被迫开源”?
开发语言·qt·开源
凯子坚持 c5 小时前
Qt常用控件指南(2)
服务器·数据库·qt
少控科技21 小时前
QT新手日记028 QT-QML所有类型
开发语言·qt
hqwest1 天前
码上通QT实战37--项目总结
开发语言·qt·软件开发·系统集成·设备选型