пользователей: 30398
предметов: 12406
вопросов: 234839
Конспект-online
РЕГИСТРАЦИЯ ЭКСКУРСИЯ

Концепция сохранения - восстановления объектов. Необходимые действия по обеспечению возможности сохранения восстановления объектов класса CDocument. Разбор примера с сериализацией в классе документа.

 

Сохранение и восстановление состояния объектов

Одна из основных задач программы — сохранять данные пользователя после их изменения по той или иной причине. Без этого все усилия, которые пользователь затратил на редактирование данных, пропадут, как только приложение завершит работу. В большинстве случаев, когда приложение создается с использованием AppWizard, Visual C++ без Вашего участия включает в него программы, которые необходимы для сохранения и восстановления данных.

Одна из задач, решаемых программистом при разработке приложений, которые могут создавать и редактировать документы различных типов (например, различные редакторы), заключается в том, чтобы предоставить пользователю возможность записать внутреннее представление документа в файл и восстановить его

Процесс сохранения и восстановления внутреннего представления объекта называется сериализацией данных.

При создании приложения вам приходится иметь дело с достаточно большим разнообразием типов объектов. Одни типы объектов, хранящих данные, довольно просты, например тип int или chaг. Другие являются экземплярами классов -—строками (экземплярами класса CString) или даже объектами классов, созданными специально для данного приложения. При использовании таких, объектов в приложении, которое должно формировать, сохранять и восстанавливать документы, разработчику волей-неволей необходимо изобретать средства сохранения и восстановления этих объектов с тем, чтобы можно было их восстановить. Свойство объекта сохраняться и восстанавливаться называется живучестью (persistence). Практически все классы MFC наделены этим свойством, поскольку они прямо или косвенно происходят от базового класса CObject. Последний уже обладает базовыми функциями сохранения-восстановления объекта.

Приложения, подготовленные при помощи средства AppWizard, используют этот механизм с помощью методов класса CDocument.

Программисту предлагается только переопределить метод Serialize этого класса для работы с конкретными данными приложения.

Программист может определить свой класс (на основе базового класса CObject) для работы с данными и определить в нем механизм записи и восстановления объектов.

Создание класса, обеспечивающего сериализацию данных

Библиотека классов MFC определяет механизм записи и восстановления объектов (serialization), причем поддержка этого механизма осуществляется средствами класса CObject.

Классы, наследованные от CObject, также могут обеспечивать работу механизма записи и восстановления объектов. Для этого при объявлении класса надо указать макрокоманду DECLARE_SERIAL, а при определении - макрокоманду IMPLEMENT_SERIAL.

Макрокоманду DECLARE_SERIAL необходимо поместить в описании класса в заголовочном файле. В качестве параметра макрокоманды надо указать имя класса

 

DECLARE_SERIAL (имя_класса)

 

Макрокоманду IMPLEMENT_SERIAL следует указать перед упоминанием класса в файле исходного текста приложения. Прототип макрокоманды IMPLEMENT_SERIAL представлен ниже:

 

IMPLEMENT_SERIAL (имя_класса, имя_базового_класса, номер_версии)

 

Параметр имя_класса определяет имя класса, имя_базового_класса - имя базового класса, из которого непосредственно наследуется класс. Последний параметр номер_версии - это число типа UINT, определяющее версию программы. Если разрабатывается новая версия приложения и изменяется набор данных, которые необходимо записать в файл, нужно изменить значение параметра номер_версии.

В классе должны быть определены специальные методы для записи и восстановления состояния объектов этого класса. Обычно эти методы сохраняют и восстанавливают элементы данных из класса. Таким образом, объекты класса сами отвечают за то, как они сохраняют и восстанавливают свое состояние.

Методы, сохраняющие и восстанавливающие объекты, взаимодействуют с объектом класса CArchive, который осуществляет непосредственную запись и чтение информации из файла на диске.

Класс CObject содержит виртуальный метод Serialize, отвечающий за запись и чтение объектов классов, наследованных от класса CObject:

 

virtual void Serialize(CArchive& ar);

 

В качестве параметра ar методу передается указатель на объект класса CArchive, используемый для записи и восстановления состояния объекта класса CObject (или наследуемого от него класса). Чтобы узнать, какую операцию должен выполнить метод Serialize, необходимо воспользоваться методами IsLoading или IsStoring класса CArchive.

Итак, при создании нового класса, в котором метод Serialize применяется для сериализации данных, необходимо:

Чтобы класс был производным от класса CObject или его потомков.

При объявлении класса необходимо вставить макрокоманду DECLARE_SERIAL.

Определить в классе функцию Serialize, отвечающую за хранение переменных класса.

Определить в классе конструктор без параметров. Это может быть защищенный конструктор, если он вызывается только для процесса сериализации данных. В конструкторе возможно динамическое создание объектов и инициализация переменных, если это необходимо.

Объявить в классе деструктор, если требуется выполнить специальные действия при разрушении объектов класса, например, освободить память динамически созданных объектов.

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

Шаблон (заготовка) файлов определения и реализации класса, который обеспечивает процесс сериализации данных

 

// фрагмент файла определения класса

class CMyDoc:public CObject

         зн

                    DECLARE_SERIAL(CMyDoc)

 

          protected:

                    virtual void Serialize(CArchive& ar);

 

          protected:

                    CMyDoc();

 

          protected:

                    ~CMyDoc();

 

          // другие описания класса

          . . .

          зн;

 

// фрагмент файла реализации класса

 

          IMPLEMENT_SERIAL(CMyDoc, CObject,1)

 

          CMyDoc::CMyDoc()

          зн

                    // здесь возможно динамическое создание объектов и

                    // инициализация переменных, если это необходимо

                    . . .

          зн

 

          CMyDoc::~CMyDoc()

         зн

                    // здесь возможно выполнение специальных действий

                    // при разрушении объектов класса, например,

                    // освобождение памяти динамически созданных объектов

                    . . .

         зн

 

          void CMyDoc::Serialize(CArchive& ar)

          зн

                    if(ar.Storing())

                  зн

                              // здесь следует добавить код для записи переменных в архив

                              . . .

                   зн

                    else

                   зн

                              // здесь следует добавить код для чтения переменных из архива,

                              . . .

                    зн

 

                    // здесь следует добавить вызовы методов Serialize для переменных

                    // класса CMyDoc, являющихся объектами классов,

                    // имеющих собственные методы Serialize

                    . . .

          зн

          // другие методы класса

          . . .

Рассмотрим примеры реализации сохранения-восстановления объектов.

При создании программы с помощью AppWizard мы получаем приложение, которое использует классы документа и представления для формирования, редактирования и отображения данных. При этом объект класса документа, производного от CDocument, отвечает за хранение данных в течение всего сеанса работы приложения, а также за сохранение и загрузку данных, так что документ сохраняет свое состояние после завершения одного сеанса работы с ним и восстанавливает в начале следующего сеанса.

Рассмотрим некоторое приложение, документ которого представляет собой единственную текстовую строку, которая выводится на экран классом представления. Предусмотрим в работе приложения три команды меню. При первом запуске текст сообщения автоматически устанавливается как Default Message. Его можно изменить, выполнив команду меню Edit->Change Message. Сохранить документ можно с помощью команды File->Save, а вновь загрузить — с помощью File->Open.

Классы документа

Способность объектов сохраняться  после изменения в одном сеансе и восстанавливать свое состояние в следующем, используя файл в качестве промежуточной среды хранения, есть ничто иное, как живучесть объектов с точки зрения пользователя. Рассмотрим реализацию концепции сохранения-восстановления в приложениями, созданными с помощью AppWizard.

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

  1. Создание членов-переменных класса документа, которые будут хранить специфические для данного вида документа данные.
  1. Инициализация этих членов-переменных в методе OnNewDocument( ) класса документа.
  2. Организация отображения текущего состояния документа в методе OnDraw() класса представления.
  3. Включение в класс представления методов, обеспечивающих редактирование документа.
  4. Модификация метода Serialize() класса документа — включение в него операторов, обеспечивающих сохранение и загрузку данных, которые и представляют собой содержание документа.

Что касается МDI-приложений; то, помимо перечисленных операций, придется сделать кое-что дополнительно, поскольку нужно обеспечить правильный выбор сохраняемого и корректную загрузку восстанавливаемого документов с учетом того, что приложение такого типа работает в течение одного сеанса с множеством документов одновременно. К счастью, большую часть этих функций MFC реализует без участия программиста.

Рассмотрим SDIприложение My, построенное AppWizard и поддерживающее архитектуру документ/представление.

Определим данные, с которыми работает представление. Добавим в секцию атрибутов класса CMyDoc (в файле MyDoc.h) определение переменной m_message ,типа CString, чтобы этот фрагмент определения класса выглядел следующим образом:

 

//Атрибуты.

public:

CString m_message;

 

В данном случае документ содержит единственный объект класса CString— строку текста. В реальных приложениях данные будут значительно сложнее. Однако этой единственной строки текста хватит, чтобы продемонстрировать особенности технологии обеспечения сохранности документа. Очень часто программисты используют в классе документа открытые члены-переменные вместо закрытых членов, для каждого из которых организуется открытая функция доступа. Это несколько облегчает разработку класса представления, методы которого должны обращаться к членам класса документа. Но в дальнейшем при сопровождении программы и, в частности, ее модификации такой подход несколько усложнит жизнь.

Класс документа также должен обеспечить инициализацию данных при открытии нового документа, что возлагается на метод OnNewDocument( ) этого класса. Вызовите при помощи окна ClassView текст этой функции в окно редактора кода и отредактируйте его. Добавьте оператор инициализации строковой переменной.

 

BOOL CMyDoc::OnNewDocument()

зн    if(!CDocument::OnNewDocument())

return FALSE;

          m_message = "Default Message";

return TRUE;зн

 

После того, как переменная m_message—член класса документа— инициализирована, приложение должно вывести содержимое документа в свое окно. Здесь за дело берется метод OnDraw() класса представления. Вывести, текст этой функции в окно редактора кода можно уже известным вам способом при помощи окна Class View. После редактирования функция должна иметь вид:

 

void CMyView: :OnDraw(CDC* рDС)

зн  CMyDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

pDC->TextOut(20,20, pDoc->m_message);

зн

 

Если оттранслировать приложение My и запустить его на выполнение, вы увидите, что на экране появится сообщение Default Message.

Теперь необходимо обеспечить возможность редактирования. Для того чтобы обеспечить такую возможность, нужно включить в меню Edit приложения пункт Change Message. По этой команде должны запускаться средства, позволяющие пользователю изменить текст документа — выводимого сообщения.

Щелкните на вкладке Resource в левой части экрана и вызовите окно ResourceView, разверните компонент Menu и дважды щелкните на IDR_MAINFRAME. Теперь можно приступить к редактированию меню. Щелкните на пункте Edit и разверните его. Щелкните на пустом поле внизу списка пунктов этого меню и введите Change &Message. В результате в меню будет добавлен новый пункт.

Теперь вызовите мастер СlassWizard. Он понадобится вам для установления связи между новой командой и текстом программы. В левом окне ClassWizard должен быть подсвечен пункт ID_EDIT_CHANGEMESSAGE. Если это не так, щелкните на этом пункте. В раскрывающемся .списке справа вверху выберите CMyView. Щелкните на COMMAND в окне справа, а затем— на кнопке Add Function.

Предлагаемое мастером имя функции OnEditChangemessage () нам вполне подходит, так что щелкните на кнопке ОК в появившемся диалоговом окне. Теперь щёлкните на кнопке Edit Code — заготовка новой функции будет выведена в окне редактора кода. Ее нужно отредактировать.

 

Void CmyView::OnEditChangemessage,() .зн

CTime now = CTime::GetCurrentTime();

CString changetime = now.Format("Changed at %B %d %H:%M:%S");

GetDocument()->m_message = changetime;

GetDocument()->SetModifiedFlag();

Invalidate();зн

 

Эта функция, формирует строку соответственно текущей дате и времени и присваивает ее переменной-члену текущего объекта класса документа. Вызов метода SetModifiedFlag() класса документа сообщит приложению, что содержимое документа изменено. Если такое изменение зафиксировано, приложение будет предупреждать пользователя о наличии несохраненных изменений в текущем документе при попытке его закрыть. И последняя операция — запуск механизма обновления представления документа на экране, .который производится функцией Invalidate().

Если m_message является закрытой переменной-членом класса документа, понадобится разработать открытый метод SetMessage(), который будет самостоятельно вызывать SetModifiedFlagO. Таким образом, вы будете навсегда избавлены от необходимости напоминать программистам об обязательном обращении к этой функции при модификации объекта класса документа. В этом и состоит преимущество скрупулезного следования принципам объектно-ориентированного программирования.

Метод Serialize() класса документа должен позаботиться о сохранении-восстановлении данных документа. Текст заготовки функции Serialize(), сформиpoвaнный AppWizard, выглядит следующим образом

 

void CMyDoc::Serialize(CArchive& ar)

зн if (ar.IsStoring())

зн//зн

else    зн //TODO: сюда вставьте операторы загрузки данных.знзн

 

Поскольку в классе CString (объектом которого является переменная m_message) определены терминальные операторы >> и << для передачи данных в архив и из него, это значительно упрощает сохранение и восстановление данных в объекте класса документа. Добавьте следующий оператор в том месте, где в заготовке стоит инструктирующий комментарий:

 

ar<< m_message;

 

Аналогично в том месте текста программы, где должны стоять операторы загрузки, вставьте

 

ar>> m_message;

 

Терминальный оператор << пересылает CString m_message в архив, а терминальный оператор >> заносит данные в m_message из архива. До тех пор, пока данные документа сохраняются в члене— простой переменной (наподобие int или char) или в объекте такого класса, как CString, для которых определены соответствующие терминальные операторы, сохранение и восстановление документа выполняются очень просто. Указанные терминальные операторы определены для следующих простых типов данных.

 

•BYTE •

•WORD

•int

•LONG

•DWORD

•float

•double

 

Оттранслируйте приложение My и запустите его. Выберите в меню приложения Edit=>Change Message и убедитесь, что на экране появилась новая строка сообщения, Changed at …. Теперь выберите File->Save и введите имя файла. Опять измените текст сообщения с помощью команды Edit->Change Message. Выберите File->New— на экране появится предупреждающее сообщение о наличии в документе несохраненных изменений. Вам будет предложено сохранить их на диске прежде, чем открывать новый документ. Теперь выберите File=>0pen и введите имя ранее созданного файла документа (его можно найти и в списке в самом низу меню File). После этого на экране появится ранее сохраненный текст сообщения. Таким образом, вы можете убедиться, что приложение My сохранило документ по вашей команде, а затем восстановило его в прежнем виде.

Если в документ внесены изменения, он сохранен в файле, в него внесены новые изvенения и вызвана команда открыть файл с тем же именем, приложение My не будет выводить запрос Revert to saved document? (Вернуться к сохраненному документу?), как это делают другие программы. Вместо этого перед вами на экране будет самая последняя версия документа. Такой механизм встроен теперь в MFC. Если имя открываемого файла соответствует имени текущего, вы не сможете вернуться к прежней версии документа.


18.01.2020; 20:29
хиты: 54
рейтинг:0
для добавления комментариев необходимо авторизироваться.
  Copyright © 2013-2024. All Rights Reserved. помощь