我已经定义了一个像这样的“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”是一个无法实现的抽象类。
那么,我是否必须重新设计所有内容,或者是否有解决此问题的简单方法?
答案 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>
或类似。或者您可以只使用原始指针,但必须小心正确管理所有权。