65.9K
CodeProject 正在变化。 阅读更多。
Home

C++ 的基本撤销/重做框架

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.76/5 (14投票s)

2002年6月28日

3分钟阅读

viewsIcon

278775

downloadIcon

4644

一篇关于在 C++ 中实现撤销/重做功能的文章。

Sample Image

引言

多级撤销/重做应该是当今软件的基本功能。有很多方法可以实现多级撤销/重做。然而,一种通用且可重用的机制是使用命令模式,其信息可以在一本著名的书籍设计模式:可复用面向对象软件的基础中找到。在这里,我将展示一个 C++ 的实现,并讨论在 C++ 中实现命令模式时的问题。还提供了一个示例。

框架

这里介绍的 C++ 撤销/重做框架由两个类组成:CommandCommandManagerT(一个模板类)。这两个类是用 C++ 和 STL 编写的。

Command

Command 是一个命令(也称为操作)的抽象类,它封装了用户请求和状态(如果需要)。它的定义如下

class Command
{
public:
    virtual bool Execute() = 0;
    virtual bool Unexecute() = 0;
    virtual ~Command() { }
};

Command 类的 Execute 方法实际上执行用户请求,而 Unexecute 方法撤消 Execute 方法执行的操作。

派生您的子类以实现所需的命令。

CommandManager

CommandManager 管理命令历史记录。它包含两个堆栈,一个撤销堆栈和一个重做堆栈。

每次您需要执行命令时,您都会构造一个命令对象并调用 CommandManager.DoCommand 方法,将该对象作为参数传递。调用 CommandManager 的 UndoRedo 方法来撤销和重做。

问题和解决方案

在没有垃圾回收的编程语言(如 C++)中实现命令模式时,一个大问题是内存管理。例如,在示例绘图项目(稍后将讨论)中,当我们需要从文档中删除一个字形时,我们不能只对该字形调用“delete”,因为它必须保留在内存中才能使撤销/重做正常工作(当然,您可以使用一些机制来克服这个问题,但将对象保留在内存中是一种更直接和简单的方法)。因此,我们需要一种管理对象的方法。这个问题并非独一无二;许多撤销/重做应用程序都有类似的情况。

一种通用的方法是对对象使用引用计数。也就是说,当多个对象引用一个对象时,该对象应保留在内存中;当没有对象引用它时,应从内存中删除它。这也是 COM 用于管理内存的方式。为了掌握它的工作原理,让我们看一个示例。

在这个示例中,我们将在绘图程序中执行两个操作:添加一个字形,然后删除它。让我们一步一步地看。

步骤 1

AddCommand 中,我们将创建一个新的字形,然后将其添加到文档中(在命令的 Execute 方法中)。现在将有两个对象引用该字形,文档和添加命令。见下图。

第二步

现在我们删除该字形。将创建一个 ClearCommand,它将在其 Execute 方法中从文档中删除该字形。现在,文档对象不再引用该对象,但两个命令对象引用它。

要启用引用计数,我们必须做的是向字形对象添加两个方法:AddRefRelease。调用规则是:当一个对象需要引用该对象时,它应该调用它的 AddRef 方法;当它不再引用它时,它应该调用它的 Release 方法。

使用引用计数时要小心,因为它可能会导致难以发现的问题。关键是您必须保持清晰的头脑。

示例项目

示例项目是一个简单的绘图程序。当您在客户端矩形中按下鼠标左键时,它将添加一个椭圆;当您按下鼠标右键时,它将添加一个文本标签;当您按下“Delete”键时,它将删除最后添加的字形。该程序支持撤销和重做。

结论

在本文中,我介绍了一个用于 C++ 的基本撤销/重做框架,并讨论了在 C++ 中实现撤销/重做时的问题。源代码和示例项目非常不言自明。希望您觉得这篇文章有帮助。编码愉快!

© . All rights reserved.