Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / MFC
Article

A Basic Undo/Redo Framework For C++

Rate me:
Please Sign up or sign in to vote.
3.76/5 (14 votes)
27 Jun 20023 min read 274.6K   4.6K   62   12
An article on implementing undo/redo functionality in C++.

Sample Image

Introduction

Multi-level undo/redo should be a basic capability for today's software. There are many ways to implement multi-level undo/redo. However, a general and reusable mechanism is to use a Command Pattern, information of which could be found in a famous book, Design Patterns, Elements of Reusable Object-Oriented Software. Here I'll present an implementation for C++ and discuss problems when implementing Command Pattern in C++. A sample is also provided.

The Framework

The C++ undo/redo framework presented here is composed of two classes: Command and CommandManagerT (a template class). The two classes are written in C++ and STL.

class Command

Command is an abstract class for a command (also called an action), it encapsulates user request and states (if necessary). Its definition is as following:

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

The Execute method of Command class actually executes the user request, and Unexecute method undoes the operation performed by Execute method.

Derive your subclasses to implement desired commands.

class CommandManager

CommandManager manages the command history. It contains two stacks, an undo stack and a redo stack.

Every time you need to do a command, you construct a command object and call CommandManager.DoCommand method, passing the object as the parameter. Call CommandManager's Undo and Redo methods to undo and redo.

Problems and Solution

A big problem when implement the Command Pattern in programming languages that have no garbage collection such as C++ is memory management. For example, in the sample drawing project (will be discussed later), when we need to delete a glyph from the document, we cannot just call "delete" on the glyph, because it must remains in memory in order to let undo/redo work correctly (of course, you can use some mechanisms to overcome this problem, but keep the object in memory is a more straightforward and simple way). Thus we need a way to manage the objects. This problem is not unique; many undo/redo applications have similar conditions.

A general method is to use reference counting on the objects. That is, when more than one objects reference to an object, the object should remain in memory; when on objects reference to it, it should be removed from memory. This is also the way COM uses to manage memory. To grasp how it works, let's see a sample.

In this sample, we'll perform two operations in the drawing program: add a glyph and then delete it. Let's see step by step.

Step 1:

In the AddCommand, we'll create a new glyph, and then add it to the document (in the command's Execute method). Now there will be two objects referencing to the glyph, the document and the add command. See graph below.

Image 2

Step 2:

Now we delete the glyph. A ClearCommand will be created and it will remove the glyph from the document in its

Execute
method. Now, the document object no longer reference to the object, but the two command objects reference to it.

Image 3

To enable reference counting, what we have to do is to add two methods to the glyph object: AddRef and Release. The calling rule is: when an object needs to reference to the object, it should call its AddRef method; when it no longer reference to it, it should call its Release method.

Be careful when using reference counting because it can cause problems that are not easy to find out. The key is that you must keep a clear mind.

The Sample Project

The sample project is a simple drawing program. When you press left button in the client rectangle, it will add an ellipse; when you press right button, it'll add a text label; when you press "Delete" key, it'll delete the last added glyph. The program supports undo and redo.

Conclusion

In this article, I presented a basic undo/redo framework for C++, and also talked about problems when implementing undo/redo in C++. The source code and the sample project are very self-explanatory. Hope you'll find this article helpful. Happy coding!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
China China
Yingle Jia is a software engineer located in Beijing, China. He currently works at IBM CSDL (China Software Development Lab). His interests include C++/COM/C#/.NET/XML, etc. He likes coding and writing.

He is the creator of ACF (Another C++ Framework) project. See http://acfproj.sourceforge.net/.

He also has a blog at http://blogs.wwwcoder.com/yljia/

Comments and Discussions

 
Question适用于opengl的undo/redo吗 Pin
feng75765961110-Oct-12 15:37
feng75765961110-Oct-12 15:37 
GeneralGood jbo!Simple is greate! Pin
ja112326-Sep-12 15:42
ja112326-Sep-12 15:42 
Questionafter glyph modification Pin
transoft31-Aug-12 14:55
transoft31-Aug-12 14:55 
General这个好啊 Pin
dwangs30-May-04 17:15
dwangs30-May-04 17:15 
GeneralIt's good to know RichEdit has it already Pin
Alexandru Savescu28-Jun-02 20:42
Alexandru Savescu28-Jun-02 20:42 
GeneralRe: It's good to know RichEdit has it already Pin
fhchina17-Jan-03 4:07
fhchina17-Jan-03 4:07 
GeneralRe: It's good to know RichEdit has it already Pin
Filomela7-Jan-05 22:35
Filomela7-Jan-05 22:35 
Generalwhy all undo examples... Pin
28-Jun-02 1:28
suss28-Jun-02 1:28 
QuestionWhy here? Pin
Paul Selormey27-Jun-02 23:39
Paul Selormey27-Jun-02 23:39 
AnswerRe: Why here? Pin
James T. Johnson27-Jun-02 23:42
James T. Johnson27-Jun-02 23:42 
GeneralRe: Why here? Pin
Alexandru Savescu28-Jun-02 20:35
Alexandru Savescu28-Jun-02 20:35 
AnswerRe: Why here? Pin
Chris Maunder3-Jul-02 4:37
cofounderChris Maunder3-Jul-02 4:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.