C++ 中的抽象类问题撤消/重做实施

发布于 2024-08-17 16:29:09 字数 567 浏览 8 评论 0 原文

我定义了一个“Action”纯抽象类,如下所示:

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

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

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

Undo

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

Redo

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

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

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

I have defined an "Action" pure abstract class like this:

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

And represented each command the user can execute with a class.

For actual undo/redo I would like to do something like this:

Undo

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

Redo

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

The compiler obviously does not accept this, because "Action" is an abstract class which can not be istantiated.

So, do I have to redesign everything or is there a simple solution to this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

美人如玉 2024-08-24 16:29:09

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

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

std::vector 之所以如此,还有另一个原因。 HistoryStack; 将不起作用,这就是切片。当将派生类的对象添加到向量时,它们将被转换为基类并释放其所有多态性。有关更多信息,请参阅:什么是对象切片?

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

You should store actions as pointers, that will keep the compiler happy.

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

There is another reason why std::vector<Action> historyStack; will not work and that's slicing. When adding objects of derived classes to the vector they will be cast to the base class and loose all their polymorphism. More about it here: What is object slicing?

EDIT Look into using ptr_vector to manage the lifetime of the objects in the vector: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html

凉月流沐 2024-08-24 16:29:09

无论如何,多态调度仅通过 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);

另一个选项可以是 boost::shared_ptrstd::stack 或类似的。或者您可以只使用原始指针,但必须小心所有权的管理是否正确。

Polymorphic dispatch only happens through pointers or references in C++ anyway. You may not be able to create a value of Action, but you will find you'll be able to create references and pointers to Actions.

pop merely needs to return a (possibly smart) pointer, or a reference, to an Action. One approach might be to use std::auto_ptr and boost::ptr_deque, this will (with correct usage) ensure that the actions are appropriately cleaned up after.

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

Another option could be a std::stack of boost::shared_ptr<Action> or similar. Or you can just use raw pointers, but you must be careful that ownership is managed correctly.

谁的年少不轻狂 2024-08-24 16:29:09

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

例如:

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 与标准容器一起使用,因此您必须手动管理内存或实现其他方法。但如果您将撤消缓冲区包装在一个类中,这应该不是一个大问题。

You should store pointers to performed operations in the queue.

For instance:

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

Then:

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

And:

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

Of course you should use new and delete to create and free memory for actual Action objects and I don't think you can use auto_ptr with standard containers so you have to manage your memory manually or implement some other method. But this should not be a big problem if you wrap undo buffer in a class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文