Pergunta

Eu defini uma classe abstrata pura de "ação" como esta:

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

E representou cada comando que o usuário pode executar com uma classe.

Para desfazer/refazer real, gostaria de fazer algo assim:

Desfazer

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

Refazer

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

O compilador obviamente não aceita isso, porque "ação" é uma classe abstrata que não pode ser istantiada.

Então, eu tenho que redesenhar tudo ou existe uma solução simples para esse problema?

Foi útil?

Solução

Você deve armazenar ações como indicadores, que manterão o compilador feliz.

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

Há outra razão para que std::vector<Action> historyStack; Não vai funcionar e isso é fatiamento. Ao adicionar objetos de classes derivadas ao vetor, eles serão lançados na classe base e perderão todo o seu polimorfismo. Mais sobre isso aqui: O que é fatiamento de objetos?

EDITAR Procure usar ptr_vector para gerenciar a vida útil dos objetos no vetor: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html

Outras dicas

De qualquer forma, o despacho polimórfico acontece por meio de ponteiros ou referências em C ++. Você pode não ser capaz de criar um valor de ação, mas descobrirá que poderá criar referências e ponteiros para ações.

O Pop apenas precisa devolver um ponteiro (possivelmente inteligente) ou uma referência a uma ação. Uma abordagem pode ser usar std :: auto_ptr e Boost :: ptr_deque, isso (com uso correto) garantirá que as ações sejam apropriadamente limpas depois.

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

Outra opção pode ser um std::stack do boost::shared_ptr<Action> ou similar. Ou você pode apenas usar ponteiros cruas, mas deve ter cuidado para que a propriedade seja gerenciada corretamente.

Você deve armazenar ponteiros para executar operações na fila.

Por exemplo:

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

Então:

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

E:

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

Claro que você deve usar novo e excluir para criar e memória livre para real Ação objetos e eu não acho que você possa usar auto_ptr Com contêineres padrão, você precisa gerenciar sua memória manualmente ou implementar algum outro método. Mas isso não deve ser um grande problema se você envolver o buffer de desfazer em uma classe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top