Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Замечание
Библиотека классов Microsoft Foundation (MFC) продолжает поддерживаться. Однако мы больше не добавляем функции или обновляем документацию.
В этом примечании описываются подпрограммы MFC, поддерживающие постоянные объекты C++ и формат данных объекта при его хранении в файле. Это относится только к классам с макросами DECLARE_SERIAL и IMPLEMENT_SERIAL .
Проблема
Реализация MFC для устойчивых хранилищ данных сохраняет данные для многих объектов в одной непрерывной части файла. Метод объекта преобразует данные объекта Serialize в компактный двоичный формат.
Реализация гарантирует, что все данные сохраняются в одном формате с помощью класса CArchive. В качестве переводчика он использует объект CArchive. Этот объект сохраняется с момента его создания до вызова CArchive::Close. Этот метод можно вызвать явным образом программистом или неявно деструктором, когда программа выходит из области, содержащей объект CArchive.
Это примечание описывает реализацию CArchive элементов CArchive::ReadObject и CArchive::WriteObject. Вы найдете код для этих функций в Arcobj.cpp и основную реализацию CArchive в Arccore.cpp. Пользовательский код не вызывает ReadObject и WriteObject напрямую. Вместо этого эти объекты используются специфичными для класса операторами безопасной вставки и извлечения, которые создаются автоматически макросами DECLARE_SERIAL и IMPLEMENT_SERIAL. В следующем коде показано, как WriteObject и ReadObject неявно вызываются:
class CMyObject : public CObject
{
DECLARE_SERIAL(CMyObject)
};
IMPLEMENT_SERIAL(CMyObj, CObject, 1)
// example usage (ar is a CArchive&)
CMyObject* pObj;
CArchive& ar;
ar <<pObj; // calls ar.WriteObject(pObj)
ar>> pObj; // calls ar.ReadObject(RUNTIME_CLASS(CObj))
Сохранение объектов в хранилище (CArchive::WriteObject)
Метод CArchive::WriteObject записывает данные заголовка, используемые для восстановления объекта. Эти данные состоят из двух частей: типа объекта и состояния объекта. Этот метод также отвечает за сохранение идентичности записываемого объекта, чтобы сохранялась только одна копия независимо от количества указателей на этот объект (включая цикличные указатели).
Сохранение (вставка) и восстановление (извлечение) объектов зависит от нескольких "констант манифеста". Это значения, которые хранятся в двоичном файле и предоставляют важные сведения для архива (обратите внимание, что префикс W указывает 16-разрядные количества):
| Тег | Описание |
|---|---|
| wNullTag | Используется для указателей объектов NULL (0). |
| wNewClassTag | Указывает, что следующее описание класса является новым для этого контекста архива (-1). |
| wOldClassTag | Указывает класс считываемого объекта в этом контексте (0x8000). |
При хранении объектов архив поддерживает CMapPtrToPtr (m_pStoreMap), который сопоставляет хранимый объект с 32-разрядным постоянным идентификатором (PID). PiD назначается каждому уникальному объекту и каждому уникальному имени класса, сохраненного в контексте архива. Эти PID идентификаторы выдаются последовательно, начиная с 1. Эти постоянные идентификаторы (PIDs) не имеют значения за пределами контекста архива и, в частности, не должны путаться с номерами записей или другими идентификационными элементами.
В классе CArchive PID 32-битные, но записываются как 16-битные, если они больше 0x7FFE. Большие PID записываются как 0x7FFF, после чего следует 32-разрядный PID. Это обеспечивает совместимость с проектами, созданными в более ранних версиях.
Когда запрос выполняется для сохранения объекта в архив (обычно с помощью глобального оператора вставки), проверка выполняется для указателя CObject NULL. Если указатель имеет значение NULL, wNullTag вставляется в архивный поток.
Если указатель не имеет значения NULL и может быть сериализован (класс является классом DECLARE_SERIAL ), код проверяет m_pStoreMap , чтобы узнать, сохранен ли объект уже. Если он имеется, код вставляет 32-разрядный PID, связанный с этим объектом, в архивный поток.
Если объект не был сохранен до этого, существуют две возможности, которые следует рассмотреть: объект и его точный тип (то есть класс) являются новыми для этого архивного контекста, или объект уже является точного типа, который был встречен ранее. Чтобы определить, был ли замечен тип, код запрашивает m_pStoreMap для объекта CRuntimeClass , соответствующего объекту, связанному CRuntimeClass с сохраненным объектом. Если имеется совпадение, WriteObject вставляет тег, который является битовой OR частью wOldClassTag и этим индексом. Если CRuntimeClass новый в этом контексте архива, WriteObject назначает новый ИДЕНТИФИКАТОР этому классу и вставляет его в архив перед значением wNewClassTag.
Затем дескриптор этого класса вставляется в архив, используя метод CRuntimeClass::Store.
CRuntimeClass::Store вставляет номер схемы класса (см. ниже) и текстовое имя ASCII класса. Обратите внимание, что использование текстового имени ASCII не гарантирует уникальность архива в приложениях. Поэтому следует пометить файлы данных, чтобы предотвратить повреждение. После вставки сведений о классе архив помещает объект в m_pStoreMap , а затем вызывает Serialize метод для вставки данных для конкретного класса. Поместите объект в m_pStoreMap перед вызовом Serialize , чтобы предотвратить сохранение нескольких копий объекта в хранилище.
При возвращении к исходному вызывающому объекту (как правило, корне сети объектов) необходимо вызвать CArchive::Close. Если вы планируете выполнять другие операции CFile , необходимо вызвать CArchive метод Flush , чтобы предотвратить повреждение архива.
Замечание
Эта реализация накладывает жесткое ограничение 0x3FFFFFFE индексов для каждого контекста архива. Это число представляет максимальное количество уникальных объектов и классов, которые можно сохранить в одном архиве, но один файл диска может иметь неограниченное количество контекстов архива.
Загрузка объектов из Магазина (CArchive::ReadObject)
Метод CArchive::ReadObject используется для загрузки (извлечения) объектов и является противоположностью WriteObject. Как и WriteObject, ReadObject не вызывается напрямую кодом пользователя; код пользователя должен вызвать оператор безопасного извлечения типа, который вызывает ReadObject с ожидаемым CRuntimeClass. Это проверяет целостность типа операции извлечения.
WriteObject Так как в реализации назначены увеличивающиеся идентификаторы, начиная с 1 (0 заранее определен как объект NULL), ReadObject реализация может использовать массив для поддержания состояния архивного контекста. Если PID считывается из хранилища и идентификатор больше текущей верхней границы m_pLoadArray, это означает, что следует новый объект (или описание класса).
Номера схемы
Номер схемы, который назначается классу при обнаружении метода класса \IMPLEMENT_SERIAL, является "версией" реализации класса. Схема относится к реализации класса, а не к количеству раз, когда объект был сделан постоянным (обычно это называется версией объекта).
Если вы планируете поддерживать несколько различных реализаций одного класса с течением времени, добавив схему при пересмотре реализации метода объекта Serialize , вы сможете написать код, который может загружать объекты, хранящиеся с помощью более старых версий реализации.
Метод CArchive::ReadObject выдает CArchiveException при обнаружении номера схемы в постоянном хранилище, которое отличается от номера схемы описания класса в памяти. Из этого исключения нелегко восстановиться.
Вы можете использовать VERSIONABLE_SCHEMA в сочетании с (побитовой операцией ИЛИ) версии вашей схемы, чтобы предотвратить возникновение этого исключения. С помощью VERSIONABLE_SCHEMAкода можно выполнить соответствующее действие в его Serialize функции, проверив возвращаемое значение из CArchive::GetObjectSchema.
Вызов метода Serialize напрямую
Во многих случаях накладные расходы на общую схему архива объектов WriteObject не являются необходимыми ReadObject. Это распространенный случай сериализации данных в CDocument. В этом случае метод Serialize элемента CDocument вызывается напрямую, а не через оператор извлечения или вставки. Содержимое документа может в свою очередь использовать более общую схему архивирования объектов.
Вызов Serialize напрямую имеет следующие преимущества и недостатки:
Дополнительные байты не добавляются в архив до или после сериализации объекта. Это не только делает сохраненные данные меньше, но позволяет реализовать
Serializeподпрограммы, которые могут обрабатывать любые форматы файлов.MFC настроен таким образом, чтобы реализации
WriteObjectи связанные коллекцииReadObjectне были включены в ваше приложение, если вам не нужна более общая схема архива объектов для какой-то другой цели.Код не должен восстанавливаться из старых чисел схемы. Это делает код сериализации документа ответственным за кодирование чисел схемы, номеров версии формата файла или других идентификационных номеров, которые вы используете в начале файлов данных.
Любой объект, сериализованный с прямым вызовом
Serialize, не должен использоватьCArchive::GetObjectSchemaили должен обрабатывать возвращаемое значение (UINT)-1, указывающее, что версия неизвестна.
Так как Serialize вызывается непосредственно на вашем документе, субобъектам документа обычно не удается сохранить ссылки на их родительский документ. Этим объектам должно быть явно предоставлено указатель на документ контейнера, иначе необходимо использовать функцию CArchive::MapObject для сопоставления CDocument указателя с PID перед архивированием этих обратных указателей.
Как отмечалось ранее, вы должны закодировать сведения о версии и классе самостоятельно при вызове Serialize напрямую, что позволяет изменить формат позже, сохраняя обратную совместимость со старыми файлами. Функцию CArchive::SerializeClass можно вызывать явно перед непосредственной сериализацией объекта или перед вызовом базового класса.
См. также
Технические примечания по номеру
Технические заметки по категориям