Неразумно хранить итераторы внутри этого класса? Как еще перебрать эту последовательность? - PullRequest
1 голос
/ 10 января 2010

Предупреждение, это длинный вопрос!

Я реализую карточную игру Solitaire на C ++ на Win32, и после того, как я задаю этот вопрос, становится ясно, что мне может потребоваться немного рекомендаций относительно фактического дизайна класса, а не деталей реализации.

Для реализации игры я использую шаблон контроллера вида модели. Модель - игра, карты, колонки и история ходов. Представление отвечает за рисование на экране, а управление отвечает за обработку сообщений и таймер.

Аспект игры, которую я сейчас пытаюсь реализовать, - это история действий, которая относится к Model - я хочу иметь возможность "отменять" и "повторять" любые Action.

Я описываю объект Move как атомное перемещение одной карты из одной CardPile в другую. И я описал Action как состоящий из одного или нескольких Moves. например сделка будет 10 Moves от Deck до конкретного Column. (Deck и Column - это просто специализации CardPile).

Я определяю Действие как деку Moves и предоставил некоторые функции для GetCurrentMove () и Advance () для перемещения, когда оно выполнено.

class Action
{
 public:
    void SetMoves( std::deque<Move> dmoves){ _m_deque = dmoves; }
    void Advance();
    std::deque<Move>::const_iterator GetCurrentMove();
 private:
    std::deque<Move> _m_deque;
    std::deque<Move>::const_iterator currentmove;
};

При обработке (или настройке, или отмене) эти Move объекты являются данными для анимации. Мне нужно иметь доступ к одному Move объекту одновременно. Я извлекаю Move, анализирую его в координаты x, y, а затем запускаю анимацию, чтобы переместить карту из одного места в другое на экране. Когда карта достигает места назначения, я вытаскиваю еще одну Move из очереди.

Другие люди с большим опытом советуют мне не хранить итераторы в классе Action. STL этого не делает, и для этого есть веские причины.

Но мой вопрос - разве у меня нет для хранения итераторов внутри класса Action? Вы видите, что и для моей модели, и для представления необходим доступ к текущему Move, поэтому где еще я могу сохранить тот итератор, который ссылается на текущий Move ... внутри контроллера?

Моя игровая анимация основана (очень широко) на этой модели:

void control.game_loop( message )
{
   switch( message )
   {
      case TIMER:
      {
          if( view.CardHasReachedDestination() )
          {
             game.AdvanceAnimation();
             if( !game.AnimationFinished() )
                 view.Parse(game.GetNextMove());
          }
          view.MoveCardXY();
          view.PaintToTheScreen();
          controller.StartTheTimerAgain();
      }
   }
}

С наилучшими пожеланиями,

BeeBand

Ответы [ 2 ]

1 голос
/ 11 января 2010

Я бы создал шаблон функции для анимации всего действия, а не только одного перемещения за раз. Вызвать это начальными и конечными итераторами для действия, которое нужно анимировать:

template <class iterator>
void animate_action(iterator first, iterator last) { 
    for (iterator i=first; i!=last; ++i)
        animate_move(*i);
}

Где animate_move - это почти то, что вы уже использовали для демонстрации анимации одного движения. Вы бы вызвали это что-то вроде:

animate_action(action.begin(), action.end());

или для анимации в обратном порядке:

animate_action(action.rbegin(), action.rend());

Именно поэтому (большая часть) вы хотите сделать animate_action шаблоном - таким образом, он не знает и не заботится, получает ли он прямой итератор или reverse_iterator.

Редактировать: Основываясь на дальнейших комментариях, кажется, есть несколько альтернатив.

Стандартным резервом будет использование отдельного потока для обработки анимированного рисунка, поэтому он будет иметь что-то вроде:

while (current_position != final_position) {
    draw_card(currrent_position);
    current_position = next_position();
    sleep(timer_period);
}

Другой вариант - вместо ожидания срабатывания таймера и запроса текущего итератора в этот момент, я бы хотел создать и поставить в очередь объект, представляющий каждое движение в анимации, а затем, когда таймер срабатывает, таймер Функция извлекает и выполняет следующий элемент в очереди:

for (int i=0;i<Move.size(); i++)
    for (int j=0; j<num_positions; j++)
       enqueue(move, Move[i], position(j));
0 голосов
/ 10 января 2010

Если вы используете список вместо deque, итераторы, указывающие на элемент, будут оставаться действительными в течение всего времени существования элемента.

Если вы сделаете это, я не вижу ничего плохого в хранении итераторов в этом случае.

...