我用C ++编写了一个文本编辑器程序,它有简单的命令:LEFT,RIGHT,HOME,END,BACKSPACE,DELETE,INSERT,现在我需要执行UNDO和REDO函数。在我的程序中,用户必须能够撤消不超过最后十个命令。我想使用矢量实现来实现这一点,但我不知道如何设置它。我不确定如何将光标位置和字符存储到矢量中。有人可以提供一些帮助吗?
#ifndef CURSOR_H
#define CURSOR_H
#include <stdlib.h>
#include <iostream>
template <class Object>
class Cursor;
// Incomplete Declaration
template <class Object>
class CNode
{
public:
CNode( const Object & theElement = Object( ), CNode * n = NULL ) : element( theElement ), next( n ) { }
Object element;
CNode *next;
friend class Cursor<Object>;
};
template <class Object>
class Cursor
{
public:
Cursor( );
bool isEmpty( ) const;
void makeEmpty( );
void left ( );
void right ( );
void del ( ); //This is the delete operation. I named it del instead of delete as delete conflicts with a C++ keyword.
void back ( );
void insert( const Object & x );
void home ( );
void end ( );
void undo ( );
private:
void printText ( ) ;
CNode<Object> *header;
CNode<Object> *cursorPosition;
};
//#include "Cursor.cpp"
#endif
答案 0 :(得分:3)
您想使用deque
,以便可以从正面或背面添加和删除;添加命令时,将其添加到后面,撤消时将其从后面删除,当你达到11个命令时,从前面删除一个命令。
答案 1 :(得分:2)
它存在于这个非常具体的要求。您可能必须将其与其他设计模式(例如Command,Iterator,FlyWeight等)结合使用
Memento Intent
不违反封装, 捕获并外化对象 内部状态使对象可以 稍后恢复到这个状态。
命令意图
封装请求 作为一个对象,从而让你 使用不同的参数化客户端 请求,队列或日志请求,以及 支持可撤销操作。
答案 2 :(得分:1)
还需要考虑其他一些事项:
通常,您不希望对游标移动应用撤消/重做(即它们不会影响十个命令限制的限制)。撤消/重做删除或插入文本时,当然必须在执行操作之前将光标放在适当的位置。如果用户在不执行任何光标移动或更正(退格)的情况下键入多个字符,则在应用撤消/重做时通常会将这些字符视为单个单元。
答案 3 :(得分:1)
祝贺包含undo / redo。这是任何编辑器的一个很棒的功能。它仍然会变得棘手。这里有一些想法(所有的挥手,没有代码)。
我建议您了解Command Design Pattern。你想要做的是设计一个'Command'类,其中一个实例可以“执行”一个命令(比如插入字母'A'),以及“撤消”本身。
当用户调用某个命令(比如添加字母'A')时,你'新'命令,定义其“Do”以插入'A',同时定义其“Undo”以删除A,然后添加它在撤消列表的顶部,然后“执行”。
不要将你的地址仅限制在10.为什么不把它设为无限?
无论您使用哪种结构来制作可撤销命令列表,通常的行为是,如果您已撤消某个级别,然后在该点开始编辑,则应丢弃当前级别之上的所有重做
答案 4 :(得分:0)
对于您希望能够撤消的每个操作(即推测插入,退格和del,但不是光标移动),我们可以列出“撤消”过程:
不幸的是,你的游标使用指针,当你撤消删除/退格时,新分配的CNode可能与之前的地址不同,这可能会使尝试使用该指针地址的先前撤消步骤无效。选项包括:
一些额外的数据结构,用于跟踪哪些指针以这种方式失效,如果重新创建相应的元素(痛苦),则用新值重新填充它们
找到一种更确定的方法来在CNode列表中找到正确的索引
(因为你的Cnode列表看起来没有双重链接,所以我不知道如何通过标题元素中的“文档”没有非常痛苦的重复向左移动......?)
在整理出这个索引/光标移动问题后,您必须在:
之间做出决定每次操作后,使用deque保存撤消信息:
结构历史 { 撤消操作的指示器(例如enum Op {left,right,insert,del ...}) 仅用于插入操作:对象值 }
然后有一些on-undo处理函数读取这些历史记录并协调它们描述的操作,或者
执行操作时,将函数对象推送到对deque和redo操作进行编码的deque上(对Cursor对象方法的调用),以便实际执行撤消或重做操作只涉及执行该对象(即撤消/重做操作是在编辑时分配的“黑匣子”)/这是更优雅和灵活,但可能不太熟悉初级/中级程序员所以可能更难走吧boost库具有良好的支持功能。
答案 5 :(得分:0)
我同意其他人在执行“执行”命令时捕获可撤销命令。
我还建议定期处理列表并组合撤消命令。
例如,如果您的撤消命令是:
删除A,删除B,删除C,左光标,左光标,右光标,左光标。
将其转换为仅
删除“ABC”,左光标(2)。
这样,当用户撤消操作时,他们看不到每次按键击中。相反,撤消发生在逻辑组中。