【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 的撤销系统既灵活又易于使用。

相关推荐
Quz13 小时前
QML 信号与槽:直接绑定与跨文件通信
qt·编程语言·设计
GIS阵地14 小时前
QgsSingleBandPseudoColorRenderer 完整详解(QGIS 3.40.13 C++)
开发语言·前端·c++·qt·qgis
慧都小妮子15 小时前
Aspose.CAD for .NET 26.6 更新发布
pdf·.net·3d渲染·cad·pdf导出·ifc编辑
丰锋ff17 小时前
1.2 Qt常用基础类型
开发语言·qt
nianniannnn18 小时前
Qt QMessageBox知识点
开发语言·数据库·qt
J_yyy18 小时前
【Qt核心组件类框架】
开发语言·qt
Drone_xjw19 小时前
双网卡下Qt程序数据包“乱飞”之谜:为什么必须断开再连接
开发语言·qt
Quz20 小时前
QML 网格与流式布局:Grid/GridLayout 与 Flow
qt
qq_4017004120 小时前
Qt容器性能优化:QVector、QHash、QMap到底应该怎么选?
开发语言·qt·性能优化
Quz21 小时前
QML 线性布局:Row/RowLayout 与 Column/ColumnLayout
前端·qt