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

相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能13 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G13 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt