2010-01-12 80 views
2

我已经定义“操作”纯粹的抽象类是这样的:抽象类问题在C++撤消/重做实施

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); 

编译器显然不能接受这一点,因为“行动”是一个抽象的类别,不能孤立。

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

回答

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管理向量中的对象的生命周期:反正通过C++指针或引用http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html

+1

不要忘记删除。智能指针和指针特定的容器(如Boost指针容器)相辅相成。 – GManNickG 2010-01-12 00:59:25

+0

是的,谢谢,这里肯定会需要一个智能指针容器。 – 2010-01-12 01:00:17

+0

虽然这些功能你想要一个'堆栈',而不是'矢量'。 :) – GManNickG 2010-01-12 01:11:52

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); 

当然,你应该使用删除创建和释放内存为实际动作对象,我不认为你可以使用auto_ptr与标准容器,所以你必须手动管理你的内存或实现一些其他方法。但是,如果在一个类中包装撤消缓冲区,这应该不是一个大问题。

+0

或者boost :: shared_ptr 或者std :: tr1 :: shared_ptr 如果你想对内存任务保持谨慎。 Theres很多建议不要在STL容器中存储原始指针,除非你真的不能避免它。 – 2010-01-12 01:00:48

+0

除了原始指针难以处理的标准版本之外,是否有任何特别的原因需要这样的推荐? – 2010-01-12 01:06:41

+0

可能不是。难以处理意味着容易处理,这也节省了时间。 – GManNickG 2010-01-12 01:19:09

0

态分派才会发生。您可能无法创建Action的值,但您会发现可以创建引用和指向Actions的指针。

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

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

另一种选择可能是boost::shared_ptr<Action>或类似一个std::stack。或者您可以使用原始指针,但您必须小心所有权得到正确管理。

+1

请注意,您不应该将auto_ptr存储在STL容器中。 当容器自动调整大小时,数据可能会丢失。请参阅http://www.devx.com/tips/Tip/13606请注意Logan并不是在暗示这一点,但很容易犯这个错误。 – 2010-01-12 01:07:36