![]() |
|||||
![]() ![]() |
|||||
![]() |
![]() |
![]() |
![]() |
![]() |
|
|
5.5. Сериализация. CArchive.Необходимость в хранилище постоянных данных создает дополнительные трудности при разработке объектно-ориентированных приложений. Команде разработчиков приходится решать, как сохранять структуру и взаимосвязи объектов приложения при записи и восстановлении постоянных данных. Решение этой проблемы - нетривиальное занятие, ведь обычно данные файла записываются в виде неструктурированного двоичного потока. Каркас MFC -приложения помогает разрешить эту непростую ситуацию, предлагая сериализацию (serialization) - метод сохранения структуры данных прикладных объектов при записи и восстановлении из архива постоянного хранения. Встроенная в MFC сериализация обеспечивается классом CObject . Необходимо, чтобы все другие поддерживающие сериализацию классы были производными от CObject и перегружали функцию CObject::Serialize() . Задача Serialize() заключается в архивировании отдельных данных класса и сохранении и восстановлении их из объекта MFC -класса CArchive . CArchive служит посредником между сериализуемым объектом и средой хранения данных и всегда связывается с объектом CFile . Обычно последний представляет дисковый файл, но ничто не запрещает ему представлять и файл в памяти. Например, можно связать CArchive с объектом CSharedFile для сериализации данных в буфере (или из буфера) обмена Windows . Кроме того, объект CArchive поддерживает механизм буферизации, сохраняющий типы при чтении и записи сериализуемых объектов в и из объекта CFile . Отдельно взятый объект CArchive можно применять либо для хранения данных, либо для их загрузки, но никогда - для обеих операций одновременно. Время жизни CArchive ограничено одним проходом записи в файл или чтения объектов из файла. Для сериализации в файл и восстановления из файла создаются отдельные объекты CArchive . Предназначение CArchive (запись или чтение) можно определить, обратившись к функции CArchive::IsStoring() , возвращающей булево значение. В классе CArchive определены операторы помещения в поток (<<) и извлечения из потока (>>) . Они применяются так же, как и в стандартных классах потоков C++ . Это проиллюстрировано в следующем коде: if (ar. IsStoring()) { ar << m_string; } else { ar >> m_string; } Вы можете использовать их для сохранения или извлечения данных из объектов CArchive . Ниже приведены типы данных и объекты, совместимые с операторами помещения в поток и извлечения из потока. CObject* SIZE и CSize Float WORD CString POINT и CPoint DWORD BYTE RECT и CRect double LONG CTime и CTimeSpan int COleCurrency COleVariant COleDateTime COleDateTimeSpan Напомним, что данные приложения сохраняются в объекте документа. Эти данные сериализуются в дисковый файл документа, а затем восстанавливаются в этот объект. Тип файла Вы связываете с приложением, задав расширения в диалоговом окне Advanced Options в окне 4 мастера АррWizard. Объект документа начинает сериализацию данных приложения в ответ на выбор пользователем соответствующих команд из меню работы с файлами. Каркас создает объект CArchive надлежащего типа (в зависимости от того, сохраняются или восстанавливаются архивные данные) и передает его в качестве параметра в функцию документа Serialize() . Мастер AppWizard создает в классе документа на месте Serialize() функцию-заглушку, которую требуется "наполнить" кодом, отвечающим за сохранение и восстановление постоянных членов-данных. Простыми членами-данными можно манипулировать, применяя операторы << и >>. Если в документе есть более сложные объекты, в которых предусмотрена собственная сериализация, Вы обязаны вызвать для них соответствующие функции Serialize() , передавая ссылку на текущий архив. В качестве примера рассмотрим приложение TestАрр , в котором имеется класс документа с тремя членами-данными: Class CTestAppDoc { CString m_string; DWORD m_dwVar; MyObj m_obj; } Допустим, что MyObj - класс, поддерживающий сериализацию. В приведенном ниже коде показан пример функции Serialize() для класса документа TestApp: void CTestAppDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { ar << m_string; ar << m_dwVar; } else { ar >> m_string; ar >> m_dwVar; } m_obj.Serialize(ar); } Обратите внимание, что функция MyObj::Serialize() вызывается из операторов условного перехода - в ней самой заданы условия ветвления в зависимости от режима работы с данными (записи или чтения). На рисунке 1 показано, как можно применить эту методику для рекурсивной сериализации. Непротиворечивость процедур сериализации гарантирует упрощение манипуляций со сложными объектами - с сохранением их на диске и обратным восстановлением в приложении. Вы обязаны обеспечить соответствие сохраняющих и восстанавливающих ветвей Вашей функции Serialize() - они должны работать с одними и теми же объектами и в одном и том же порядке. Процедуры сериализации гарантируют корректную "реконструкцию" структуры объекта при ее восстановлении из дискового файла. Это обеспечивается сохранением сведений не только о типе объекта, но и о его текущем состоянии (значениях членов-данных). При восстановлении соответствующая процедура использует эту информацию для определения типа и создания объекта, принимающего данные. Тем не менее Вам надо гарантировать корректность создания объекта, предоставив для своего сериализуемого класса конструктор по умолчанию (без параметров).
|