C ++ undo / redo实现中的抽象类问题

时间:2010-01-12 00:52:12

标签: c++ inheritance abstract-class undo-redo

我已经定义了一个像这样的“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”是一个无法实现的抽象类。

那么,我是否必须重新设计所有内容,或者是否有解决此问题的简单方法?

3 个答案:

答案 0 :(得分:8)

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

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

还有另一个原因导致std::vector<Action> historyStack;不能正常工作而且正在切片。将派生类的对象添加到向量时,它们将被强制转换为基类并释放所有多态性。更多相关信息:What is object slicing?

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

答案 1 :(得分: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); 

当然你应该使用 new delete 为实际的 Action 对象创建和释放内存,我认为你不能使用带有标准容器的 auto_ptr ,因此您必须手动管理内存或实施其他方法。但是,如果将undo buffer包装在一个类中,这应该不是一个大问题。

答案 2 :(得分: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::stack boost::shared_ptr<Action>或类似。或者您可以只使用原始指针,但必须小心正确管理所有权。