Программа "Крестики-нолики", подготовкой которой мы занимались в предыдущих статьях этой серии, практически завершена - осталось лишь уточнить некоторые детали.
В процессе создания этой несложной Windows-программы мы приобрели неплохой опыт в разработке таких программ на базе MFC. Мы познакомились с концепцией создани программ на базе двух классов - document и view - и тем, как они используются при составлении большинства программ на Visual C++. Мы рассмотрели и вопрос о том, какие изменения вносятся в подготовленные Мастером AppWizard классы document и view и как приспособить их для нужд совей программы. В этой заключительной статье мы должны обеспечить пользователям возможность сохранения и возобновления игры. Применение готовых, имеющихся в MFC функций значительно упрощает нашу задачу, которая сводится в итоге к добавлению в текст программы всего лишь нескольких строк.
При создании Windows-программ без привлечения MFC или других аналогичных базовых средств разработки подготовка процедур обработки команд меню File (Файл) - Open (Открыть), Save (Сохранить), Save As (Сохранить как) - может оказаться довольно трудоемкой задачей. Однако в среде Visual C++ это делается на удивление просто. Основную работу по реализации этих функций выполняют средства библиотеки классов. В выведенном на экран диалоговом окне Open или Save As вы вводите им файла, после чего функции MFC открывают файл для чтени или записи. Чтобы избавить программиста от некоторых сложностей операций ввода/вывода файлов, средствами MFC создается объект архива на базе MFC-класса CArchive, для которого организуется связь с самим файлом. После этого вызывается виртуальная функция Serialize, принадлежащая классу document, и в качестве аргумента ей передается ссылка на этот CArchive-объект.
Единственное, что требуется от нас, - предусмотреть для программы на Visual C++ средства, позволяющие ей сохранять и загружать свои рабочие документы, а дл этого внести изменения в функцию Serialize(), принадлежащую уже созданному классу document, и добавить к ней операторы пересылки рабочих данных в объект CArchive или из него. Мастер AppWizard уже подготовил базовый вариант функции Serialize(), котора обращается к функции CArchive::IsStoring(), чтобы выяснить, пополняется ли архив за счет пересылаемого в него документа (сохранение) или документ считывается из него (загрузка). Кроме этого, в MFC предусмотрены операторы извлечения и вставки: оператор и << и оператор >>, с помощью которых можно пополнять данными простых типов экземпляр CArchive или считывать из него. Если переменной для Carchive-объект дать имя ar, тогда с помощью следующего предложения можно записать в архив две переменные (с типом int) - m_nIndex и m_nVal:
ar << m_nIndex << m_nVal;
Считывание этих переменных из архива выполняетс тоже просто:
ar >> m_nIndex >> m_nVal;
Чаще всего дополнение программы на Visual C++ средствами для сохранения и загрузки данных - процедура простая. Для более сложных программ можно даже подготовить специальные сохраняемые классы, перенос которых в архив происходит так же просто, как пересылка данных типа int и других элементарных типов.
В части 3 было показано, как организовано хранение данных документа Tic в двух компонентных переменных: в массиве размерностью 5х5, содержащем элементы типа BYTE со сведениями о внесенных пользователем значках Х и О, и переменной типа BOOL с информацией об очередности хода.
Таким образом, задача сохранения рабочих данных программы "Крестики-нолики" сводится просто к записи значений двух названных переменных. На лист. 1 показано, как будет выглядеть функция Serialize() класса document после внесения изменений, предназначенных для передачи переменных CTicDoc::m_grid и CTicDoc::m_bXsTurn в архив и обратно. Значения, содержащиеся в массиве m_grid, пересылаютс последовательно один за другим при выполнении вложенного цикла for; затем сразу же обрабатываетс переменная m_bXsTurn. Не имеет принципиального значения, какие данные переправляются в первую очередь, надо только соблюдать порядок. Если в архив отправляется сначала m_bXsTurn, а затем m_grid, тогда чтение из архива следует производить в аналогичном порядке.
Лист. 1. Функция Serialize() класса document.
void CTicDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { for (int i=0; i<5; i++) for (int j=0; j<5; j++) ar << m_grid[i][j]; ar << m_bXsTurn; } else { for (int i=0; i<5; i++) for (int j=0; j<5; j++) ar >> m_grid[i][j]; ar >> m_bXsTurn; } }
Внесите изменения в функцию CTicDoc::Serialize(), чтобы придать ей вид, близкий к показанному на лист. 1, и заново скомпонуйте программу. Теперь проведите ее тестирование: внесите несколько значков Х и О рабочий документ, сохраните его на диске, выбрав в меню команду File|New, создайте новый документ и выполните повторную загрузку сохраненного рабочего документа. В этом документе все имевшиеся Х и О должны сохраниться. А поскольку кроме m_grid сохранялась также переменна m_bXsTurn, в этом документе должна быть и верна информация об очередности хода.
Чтобы исключить вероятность случайных потерь рабочих данных, корректно составленные Windows-программы должны предусматривать средства выдачи пользователю сообщений о необходимости сохранения еще не записанных данных, если программа получает команду завершить работу. В программах архитектуры document/view за решение этой небольшой задачи отвечают средства MFC, но только при том условии, если имеется информация о наличии в рабочем документе еще не сохраненных данных.
Для того чтобы оповестить MFC-механизм о подобном факте, следует обращаться к функции SetModifiedFlag() класса document, всякий раз когда в данные вносятс изменения. Она выставляет внутри класса document некий флажок, который проверяется средствами MFC перед завершением работы программы или при закрытии текущего документа. Если этот флажок выставлен, MFC выводит сообщение пользователю о присутствии еще не сохраненных изменений и предложение сохранить документ до начала дальнейшей обработки. После записи изменений флажок автоматически возвращается в исходное состояние, а класс document помечается как "чистый" (не имеющий изменений) до следующего обращения к SetModifiedFlag().
В части 3 этой серии статей мы добавили в наш класс document две новые функции - AddX() и AddO(), к которым происходят обращения из класса view для внесени значков Х или О на игровое поле. Чтобы завершить разработку нашей программы, следует дополнить эти функции средствами для обращения к функции SetModifiedFlag(). На лист. 2 представлены обновленные варианты этих функций. Теперь, когда операторы вызова функции SetModifiedFlag() предусмотрены, можно средствами MFC проводить проверку значения флажков изменений, чтобы выяснить, имеются ли в рабочем документе неучтенные изменения. И предупреждение пользователю в случае их присутствия в классе document будет организовано средствами MFC.
Лист. 2. Функции AddX() и AddO() после внесения в них изменений.
void CTicDoc::AddX (int i, int j) { if ((i >= 0) && (i <= 4) && (j >= 0) && (j <= 4)) { m_grid[i][j]=1; // Добавить в клетку X SetModifiedFlag (); // Выставить флажок изменений // в классе document m_bXsTurn=FALSE; // Следующий ход за O } } void CTicDoc::Add0 (int i, int j) { if ((i >= 0) && (i <= 4) && (j >= 0) && (j <= 4)) { m_grid[i][j]=2; // Добавить в клетку O SetModifiedFlag (); // Выставить флажок изменений // в классе document m_bXsTurn=TRUE; // Следующий ход за X } }
Вы можете удостовериться в том, что программа работает, как задумано: перекомпонуйте ее, запустите на исполнение новую версию, добавьте несколько значков Х и О, а затем выберите в меню File команду Exit (Выход). Прежде чем завершится работа программы, вы получите подготовленное средствами MFC предупреждение о наличии еще не сохраненных данных с предложением либо сохранить их, либо продолжать выполнение программы.
Теперь наша программа вполне работоспособна. Добавим лишь один штрих: переключитесь на окно Project Workspace (Рабочая среда проекта), щелкните на вкладке ResourceView (Список ресурсов), и перед вами окажетс перечень используемых в программе меню, пиктограмм и прочих ресурсов. Щелкните дважды в списке пиктограмм на элементе IDR_MAINFRAME и с помощью встроенного редактора пиктограмм Visual C++ придайте ей свой неповторимый облик. (Не забудьте выполнить эту процедуру с обоими вариантами пиктограммы - стандартной и уменьшенной!) После повторной компоновки запустите программу. Новый вариант пиктограммы должен появитьс на панели задач Windows, обычно расположенной в нижней части экрана.
Перед тем как завершить нашу серию статей, давайте еще раз кратко перечислим этапы создания этой программы. В первой части мы с помощью Мастера AppWizard создали ее базовый текст. Во второй части мы подготовили функцию OnDraw() класса view, предназначенную для прорисовки игрового поля; затем с помощью Мастера ClassWizard добавили в класс view функцию OnLButtonDown () для обработки такого события, как щелчки левой клавишей мыши на клетках игрового поля. Далее мы дополнили класс document принадлежащими функциями, которые вызываются из класса view дл внесения в клетку значка Х или О, для выяснени очередности хода и для получения информации о том, что содержится в конкретной клетке - Х или О. В предпоследней части мы занимались вопросами взаимодействия классов document и view, а именно добивались того, чтобы при щелчке на пустой клетке функции класса view помещали в массив класса document Х или О и отображали соответствующий значок в рабочем окне; для выполнения прорисовки Х и О мы внесли изменения в функцию OnDraw(). В данной заключительной части мы описали мезанизм, на основе которого функции класса document сохраняют и повторно загружают игру.
Вот мы и перечислили этапы. Я надеюсь, что материалы, изложенные в этих статьях, дали вам какое-то представление о разработке Windows-программы на базе MFC. Tic - это достаточно простая программа, но предусмотренные в ней элементы вы обнаружите и в более внушительных и сложных программах. Окончательный вариант нашей программы, а также ее исходный текст можно получить в службе PC Magazine Online. Загрузите в свой ПК архивный файл Tic.zip и распакуйте его с помощью команды PKUNZIP (указав параметр -d), чтобы восстановить программу и ее исходный текст на вашем жестком диске. Найдите в меню File системы Visual C++ команду Open Workspace (Открыть рабочую среду), чтобы открыть этот проект и внести в него нужные вам изменения.
Джефф Просис - внештатный редактор журнала PC Magazine.