Проблема абстрактных классов в C ++ отмена / повтор реализации - PullRequest
2 голосов
/ 12 января 2010

Я определил чистый абстрактный класс "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" - это абстрактный класс, который не может быть истанирован.

Итак, я должен все переделать или есть простое решение этой проблемы?

Ответы [ 3 ]

8 голосов
/ 12 января 2010

Вы должны хранить действия как указатели, которые будут радовать компилятор.

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

Есть еще одна причина, по которой std::vector<Action> historyStack; не будет работать, и это нарезка. При добавлении объектов производных классов к вектору они будут преобразованы в базовый класс и потеряют весь свой полиморфизм. Подробнее об этом здесь: Что такое нарезка объектов?

EDIT Изучите использование ptr_vector для управления временем жизни объектов в векторе: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html

0 голосов
/ 12 января 2010

Полиморфная отправка в любом случае происходит только через указатели или ссылки в C ++. Возможно, вам не удастся создать значение Action, но вы обнаружите, что сможете создавать ссылки и указатели на действия.

pop просто необходимо вернуть (возможно, умный) указатель или ссылку на действие. Одним из подходов может быть использование 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> или аналогичный. Или вы можете просто использовать необработанные указатели, но вы должны быть осторожны, чтобы владение управлялось правильно.

0 голосов
/ 12 января 2010

Вы должны хранить указатели на выполненные операции в очереди.

Например:

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 со стандартными контейнерами, поэтому вам нужно вручную управлять памятью или использовать другой метод. Но это не должно быть большой проблемой, если вы оберните буфер отмены в классе.

...