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






3.76/5 (14投票s)
2002年6月28日
3分钟阅读

278775

4644
一篇关于在 C++ 中实现撤销/重做功能的文章。
引言
多级撤销/重做应该是当今软件的基本功能。有很多方法可以实现多级撤销/重做。然而,一种通用且可重用的机制是使用命令模式,其信息可以在一本著名的书籍设计模式:可复用面向对象软件的基础中找到。在这里,我将展示一个 C++ 的实现,并讨论在 C++ 中实现命令模式时的问题。还提供了一个示例。
框架
这里介绍的 C++ 撤销/重做框架由两个类组成:Command
和 CommandManagerT
(一个模板类)。这两个类是用 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 的 Undo
和 Redo
方法来撤销和重做。
问题和解决方案
在没有垃圾回收的编程语言(如 C++)中实现命令模式时,一个大问题是内存管理。例如,在示例绘图项目(稍后将讨论)中,当我们需要从文档中删除一个字形时,我们不能只对该字形调用“delete”,因为它必须保留在内存中才能使撤销/重做正常工作(当然,您可以使用一些机制来克服这个问题,但将对象保留在内存中是一种更直接和简单的方法)。因此,我们需要一种管理对象的方法。这个问题并非独一无二;许多撤销/重做应用程序都有类似的情况。
一种通用的方法是对对象使用引用计数。也就是说,当多个对象引用一个对象时,该对象应保留在内存中;当没有对象引用它时,应从内存中删除它。这也是 COM 用于管理内存的方式。为了掌握它的工作原理,让我们看一个示例。
在这个示例中,我们将在绘图程序中执行两个操作:添加一个字形,然后删除它。让我们一步一步地看。
步骤 1
在 AddCommand
中,我们将创建一个新的字形,然后将其添加到文档中(在命令的 Execute
方法中)。现在将有两个对象引用该字形,文档和添加命令。见下图。
第二步
现在我们删除该字形。将创建一个 ClearCommand
,它将在其 Execute
方法中从文档中删除该字形。现在,文档对象不再引用该对象,但两个命令对象引用它。
要启用引用计数,我们必须做的是向字形对象添加两个方法:AddRef
和 Release
。调用规则是:当一个对象需要引用该对象时,它应该调用它的 AddRef
方法;当它不再引用它时,它应该调用它的 Release
方法。
使用引用计数时要小心,因为它可能会导致难以发现的问题。关键是您必须保持清晰的头脑。
示例项目
示例项目是一个简单的绘图程序。当您在客户端矩形中按下鼠标左键时,它将添加一个椭圆;当您按下鼠标右键时,它将添加一个文本标签;当您按下“Delete”键时,它将删除最后添加的字形。该程序支持撤销和重做。
结论
在本文中,我介绍了一个用于 C++ 的基本撤销/重做框架,并讨论了在 C++ 中实现撤销/重做时的问题。源代码和示例项目非常不言自明。希望您觉得这篇文章有帮助。编码愉快!