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

相关推荐
LYOBOYI12311 小时前
qml练习:创建地图玩家并且实现人物移动(2)
开发语言·qt
得鹿梦鱼、14 小时前
Qt/halcon 总结halcon字典数据用法和QDataSteam序列化与反序列化结构体实例
qt·tuple字典·qdatasteam·序列化结构体·反序列化结构体
世转神风-15 小时前
qt-基础打印-不换行打印
开发语言·qt
酬勤-人间道15 小时前
CAD 曲线切割 3D 曲面:坡面 / 开挖模型的开挖 - 填埋精准计算解决方案
c++·计算机·cad·布尔计算·曲线切割·工业级解决方案·岩体
老歌老听老掉牙15 小时前
PyQt5中RadioButton互斥选择的实现方法
开发语言·python·qt
864记忆17 小时前
Qt Creator 常用命令的中英文对照表
开发语言·qt
Larry_Yanan18 小时前
Qt多进程(六)共享内存和信号量
开发语言·qt
东方忘忧18 小时前
Qt使用QDesktopServices::openUrl打开系统默认应用(如浏览器,文件,文件夹和邮件)
开发语言·qt
计算机内卷的N天18 小时前
qt的模态和非模态状态
开发语言·qt
charlie071 天前
qmake工程不能显示手动配置的Kit
qt