2

我已经定义了一个像这样的“Action”纯抽象类:

class Action {
 public:
    virtual void execute () = 0;
    virtual void revert () = 0;
    virtual ~Action () = 0;
};

并用一个类表示用户可以执行的每个命令。

对于实际的撤消/重做,我想做这样的事情:

撤消

Action a = historyStack.pop();
a.revert();
undoneStack.push(a);

重做

Action a = undoneStack.pop();
a.execute();
historyStack.push(a);

编译器显然不接受这一点,因为“Action”是一个不能被实例化的抽象类。

那么,我必须重新设计所有东西还是有一个简单的解决方案来解决这个问题?

4

3 回答 3

8

您应该将操作存储为指针,这将使编译器满意。

std::vector<Action*> historyStack;
/*...*/
historyStack.push_back(new EditAction(/*...*/));
Action* a = historyStack.pop();
a->revert();
undoneStack.push(a);

还有另一个原因std::vector<Action> historyStack;不起作用,那就是切片。当将派生类的对象添加到向量时,它们将被强制转换为基类并失去所有的多态性。更多信息在这里:什么是对象切片?

编辑查看使用 ptr_vector 来管理向量中对象的生命周期:http: //www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html

于 2010-01-12T00:57:10.030 回答
0

您应该在队列中存储指向已执行操作的指针。

例如:

std::vector<Action*> historyStack;
std::vector<Action*> undoneStack;

然后:

Action* a = historyStack.pop_back(); 
a->revert(); 
undoneStack.push_back( a ); 

和:

Action* a = undoneStack.pop_back(); 
a->execute(); 
historyStack.push_back(a); 

当然,您应该使用newdelete为实际的Action对象创建和释放内存,我认为您不能将auto_ptr与标准容器一起使用,因此您必须手动管理内存或实现其他方法。但是,如果您将撤消缓冲区包装在一个类中,这应该不是一个大问题。

于 2010-01-12T00:56:27.517 回答
0

无论如何,多态调度只能通过 C++ 中的指针或引用发生。您可能无法创建 Action 的值,但您会发现您将能够创建指向 Actions 的引用和指针。

pop 只需要返回一个(可能是智能的)指针或对 Action 的引用。一种方法可能是使用 std::auto_ptr 和boost::ptr_deque,这将(正确使用)确保在之后适当地清理操作。

std::auto_ptr<Action> a = historyStack.pop_front();
a->revert();
undoneStack.push_front(a);

另一个选项可以是std::stackofboost::shared_ptr<Action>或类似的。或者您可以只使用原始指针,但您必须小心正确管理所有权。

于 2010-01-12T01:03:30.240 回答