Las clases abstractas cuestión en C ++ deshacer / rehacer aplicación
-
20-09-2019 - |
Pregunta
he definido una "acción" clase abstracta pura como esto:
class Action {
public:
virtual void execute () = 0;
virtual void revert () = 0;
virtual ~Action () = 0;
};
Y representado cada comando el usuario puede ejecutar con una clase.
Para real de deshacer / rehacer me gustaría hacer algo como esto:
Deshacer
Action a = historyStack.pop();
a.revert();
undoneStack.push(a);
Rehacer
Action a = undoneStack.pop();
a.execute();
historyStack.push(a);
El compilador, obviamente, no se hace esto, porque "acción" es una clase abstracta que no puede ser istantiated.
Por lo tanto, tengo que rediseñar todo o hay una solución simple a este problema?
Solución
Debe almacenar acciones como punteros, que mantendrá el compilador feliz.
std::vector<Action*> historyStack;
/*...*/
historyStack.push_back(new EditAction(/*...*/));
Action* a = historyStack.pop();
a->revert();
undoneStack.push(a);
No es otra razón por la std::vector<Action> historyStack;
no va a funcionar y eso es rebanar. Cuando la adición de objetos de clases derivadas al vector serán arrojados a la clase base y suelta todo su polimorfismo. Más acerca de ello aquí: Lo que es objeto de rebanado
editar Mira en el uso de ptr_vector para gestionar el tiempo de vida de los objetos en el vector: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html
Otros consejos
despacho polimórfica sólo ocurre a través de punteros o referencias en C ++ de todos modos. Puede no ser capaz de crear un valor de acción, pero usted encontrará que usted será capaz de crear referencias y enlaces a acciones.
pop sólo tiene que devolver un puntero (posiblemente inteligente), o una referencia, a una acción. Un enfoque podría ser el uso de std :: auto_ptr y impulso :: ptr_deque , esta voluntad (con el uso correcto) garantizar que las acciones se limpian adecuadamente después.
std::auto_ptr<Action> a = historyStack.pop_front();
a->revert();
undoneStack.push_front(a);
Otra opción podría ser un std::stack
de boost::shared_ptr<Action>
o similar. O simplemente puede utilizar punteros primas, pero hay que tener cuidado de que la propiedad es administrada correctamente.
Debe almacenar punteros a las operaciones realizadas en la cola.
Por ejemplo:
std::vector<Action*> historyStack;
std::vector<Action*> undoneStack;
A continuación:
Action* a = historyStack.pop_back();
a->revert();
undoneStack.push_back( a );
Y:
Action* a = undoneStack.pop_back();
a->execute();
historyStack.push_back(a);
Por supuesto que puedes usar nueva y eliminar para crear y la memoria libre para reales Acción objetos y yo no creo que se puede utilizar auto_ptr con contenedores estándar por lo que tiene que manejar su memoria de forma manual o implementar algún otro método. Pero esto no debería ser un gran problema si usted envuelve memoria intermedia de anulación en una clase.