TN002: Постоянные формат данных объекта

В настоящей записке описываются библиотеки DLL MFC, поддерживают постоянные объекты C++ и формат данных объекта, когда он хранится в файле. Это относится только к классам с макросами DECLARE_SERIAL и IMPLEMENT_SERIAL.

Проблема

Осуществление MFC для постоянных данных основывается на компактный двоичный формат для сохранения данных для многих объектов в единый непрерывный частью файла. Это двоичный формат обеспечивает структуру для хранения данных, но это функции-члена объекта Serialize , которая обеспечивает фактические данные, сохраненные в объекте.

MFC решает проблему структуризации, используя класс CArchive. Объекта CArchive предоставляет контекст для сохранения, которая длится с момента Архив будет создан до вызова функции-члена CArchive::Close , либо явно программистом или неявно деструктор при выходе рамки, содержащие CArchive.

В настоящей записке описывается реализация ReadObject и WriteObjectчленов CArchive . ReadObject и WriteObject не вызывается напрямую, вместо этого используются специфичные для класса типизированного и операторы автоматически генерируемых макросы DECLARE_SERIAL и IMPLEMENT_SERIAL.

класс CMyObject: государственные CObject
{
 nbsp;  DECLARE_SERIAL(CMyObject)
};

IMPLEMENT_SERIAL (CMyObj, от CObject, 1)

/ / Пример использования (ar является CArchive &)
CMyObject * pObj;
CArchive и Ар;
Ар << pObj;        / / называет Ар.WriteObject(pObj)
Ар >> pObj;        / / называет Ар.ReadObject(RUNTIME_CLASS(CObj))

В настоящей записке описывается код в файл источника MFC ARCOBJ.НПК. Главная использования CArchive находятся в ARCCORE.НПК.

Сохранение объектов в хранилище (CArchive::WriteObject)

Функция-член CArchive::WriteObject пишет данные заголовка, используется для восстановления объекта. Эти данные состоит из двух частей: тип объекта и состояния объекта. Эта функция-член отвечает также за сохранение идентификации объектов, записываемых, таким образом, чтобы только одна копия сохраняется, независимо от числа указателей на объект (включая круговые указатели).

Сохранение (Вставка) и восстановление объектов (самораспаковывающийся архив) опирается на несколько «манифеста константы». Это значения, которые хранятся в двоичном формате и содержат важную информацию в архив (Примечание префикс "w" означает 16-битный количествах):

Тег Описание
wNullTag Используется для указателей объекта NULL (0).
wNewClassTag Указывает, что описание класса ниже является новой для этот архив context—(-1).
wOldClassTag Указывает, что класс объекта, считываемый рассматривать в этом контексте (0x8000).

При хранении объектов, Архив ведет CMapPtrToPtr ( m_pStoreMap) который является сопоставление от сохраненного объекта для 32-разрядных стойких идентификатор (PID). PID назначается для каждого уникального объекта и каждый уникальный класса имена, хранящиеся в контексте архива. Эти PID выдаются последовательно начиная с 1. Важно отметить, что эти PID не имеют значения в сферу архива и, в частности, не следует путать с номер записи или другие элементы идентичности.

Начиная с MFC версии 4.0 класс CArchive был расширен для поддержки очень большие архивы. В предыдущих версиях PID был 16-разрядную, ограничивая Архив 0x7FFE (32766) объектов. PID в настоящее время 32-разрядных, но они написаны как 16-битный, если только они не больше, чем 0x7FFE. Большие PID записываются как 0x7FFF, после чего 32-разрядных PID. Этот метод поддерживает обратную совместимость файлов.

Когда запрос для сохранения объекта в архив (обычно через оператор глобальной вставки), проверяется для указателя NULL CObject ; Если указатель равен NULL, wNullTag вставляется в архив поток.

Если у нас есть указатель реальный объект, способный сериализуемый (класс является DECLARE_SERIAL ), мы затем проверить m_pStoreMap чтобы узнать, если объект уже был сохранен. Если это так, мы вставить 32-разрядных PID, связанный с этим объектом.

Если объект не был сохранен до, существует две возможности, мы должны принимать во внимание: объект и точный тип объекта (то есть, класс) являются новыми для этой связи Архив, либо объект имеет точный тип уже видели. Чтобы определить, если тип видел мы запроса m_pStoreMap для объектов CRuntimeClass , соответствующий объект CRuntimeClass , связанный с объектом, который мы сохранить. Если мы уже видели этот класс раньше, WriteObject вставляет тег, который является побитовом заземлением wOldClassTag и этот индекс. Если CRuntimeClass является новой в этом контексте Архив, затем WriteObject присваивает новый PID для этого класса и вставьте его в архив, предшествуют wNewClassTag значение.

Дескриптор для этого класса затем вставляется в архив, используя функцию-член CRuntimeClass Store. CRuntimeClass::Store вставляет номер схемы класса (см. ниже) и ASCII текст имя класса. Обратите внимание, что использование ASCII текст названия не гарантирует уникальность архива несколькими приложениями, поэтому рекомендуется пометить файлы данных для предотвращения коррупции. После вставки сведений класса Архив помещает объект в m_pStoreMap и затем вызывает функцию-член Serialize для вставки данных класса в в архив. Размещение на объект в m_pStoreMap перед вызовом Serialize предотвращает несколько копий объекта сохраняются в хранилище.

При возвращении в первоначальный вызывающий объект (обычно корень из сети объектов), необходимо Закрыть Архив. Если другие CFile операции будет сделано, должна вызываться функция-член CArchive , Flush . Неспособность сделать это приведет к коррумпированной Архив.

Примечание   Эта реализация налагает жесткое ограничение 0x3FFFFFFE индексов на Архив контекста. Это число представляет максимальное количество уникальных объектов и классов, которые могут быть сохранены в одном архиве, но к сведению, что один диск файл может иметь неограниченное количество контекстов Архив.

Загрузка объектов из магазина (CArchive::ReadObject)

Загрузка (самораспаковывающийся архив) объектов использует функции-члена CArchive::ReadObject и Конверс WriteObject. Как WriteObject, ReadObject не вызывается напрямую через пользовательский код; Пользовательский код должен вызывать оператор извлечения типизированного, который вызывает ReadObject с ожидаемой CRuntimeClass. Это гарантирует целостность типа операции извлечения.

Так как WriteObject осуществление назначаются большее PID, начиная с 1 (0 предустановленные как объект NULL), реализация ReadObject может использовать массив для поддержки состояния Архив контекста. Когда PID считывается из хранилища, если PID больше текущего верхней границы m_pLoadArray, то ReadObject знает, что новый объект (или описание класса) следует.

Схема номера

Схемы номер, который назначен класс при возникновении класс IMPLEMENT_SERIAL , является "версия" реализации класса. Схема ссылается на реализацию класса, не количество раз данный объект был достигнут постоянных (обычно именуется как версия объекта).

Если вы намерены поддерживать несколько различных реализаций того же класса с течением времени, увеличивая схемы как изменить ваш объект сериализации члена реализация функции позволят вам написать код, который можно загрузить объекты, сохраненные с помощью более старых версий impleme&ntation.nbsp;

Функция-член CArchive::ReadObject создаст исключение CArchiveException , когда он встречает схемы номер в постоянное хранилище, которое отличается от схемы количество описания класса в памяти. Это не просто взять от этого исключения.

Вы можете использовать VERSIONABLE_SCHEMA или с вашей версии схемы сохранить это исключение из которых бросили. С помощью VERSIONABLE_SCHEMA, ваш код мог принимать соответствующие меры в рамках своих функций Serialize , проверив возвращаемое значение от CArchive::GetObjectSchema.

Призвание сериализовать напрямую

Существует множество случаев, когда накладные расходы общего объекта Архив схемы WriteObject и ReadObject не является необходимым или желаемого. Это распространенный случай сериализации данных в CDocument. В этом случае функции-члена Serialize CDocument вызывается непосредственно, не с экстрактом или вставить операторы. Содержимое документа в свою очередь может использовать более общую схему объекта Архив.

Непосредственный вызов Serialize имеет следующие преимущества и недостатки:

Поскольку Serialize вызывается непосредственно на ваш документ, невозможно обычно для подобъекты документа в архив ссылок на их родительский документ. Эти объекты должны уделять указатель их контейнера документа явно или вы должны использовать функцию CArchive::MapObject для сопоставления CDocument указатель PID, прежде чем эти обратно указатели архивируются.

Как отмечалось выше, необходимо кодировать информацию о версии и класса себя при вызове Serialize напрямую, позволяя изменить формат позднее при сохранении обратной совместимости со старыми файлами. Функция CArchive::SerializeClassRef может быть вызван явным образом до непосредственно сериализации объекта или перед вызовом базового класса.

Технические примечания по номеру |nbsp; Технические примечания по категориям

Index