Вернуться наверх
aco.ifmo.ru photonic
вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


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() - они должны работать с одними и теми же объектами и в одном и том же порядке.

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