这两段代码是来自 LibreCAD 的 LC_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();
}
}
构造函数逻辑:
- 初始化成员变量
- 检查有效性条件:
handleUndo参数为 truedocument指针非空viewport指针非空
- 如果有效,开始撤销周期
析构函数实现:
cpp
LC_UndoSection::~LC_UndoSection(){
try {
if (valid && viewport != nullptr) {
RS_Undoable *relativeZeroUndoable = viewport->getRelativeZeroUndoable();
if (relativeZeroUndoable != nullptr) {
document->addUndoable(relativeZeroUndoable);
}
document->endUndoCycle();
}
} catch (...) {
}
}
析构函数关键特性:
- 异常安全 :使用
try-catch(...)捕获所有异常,防止异常传播 - 添加相对零点:获取视口的相对零点(可能是坐标系原点或参考点)
- 结束撤销周期:确保撤销周期正确结束
- 空指针检查:再次检查指针有效性
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 的撤销系统既灵活又易于使用。