Compartir a través de


TN002: Formato de datos persistente de objeto

Esta nota describe las rutinas MFC que admiten objetos persistentes de C++ y el formato de los datos del objeto cuando se almacena en un archivo.Esto se aplica sólo a las clases con el DECLARE_SERIAL y IMPLEMENT_SERIAL las macros.

El problema

La implementación de MFC para datos persistentes almacena los datos de muchos objetos en una única parte contigua de un archivo.El objeto Serialize método traduce los datos del objeto en un formato binario compacto.

La implementación garantiza que todos los datos se guardan en el mismo formato utilizando el CArchive (Clase).Utiliza un CArchive objeto como un traductor.Este objeto persiste desde el momento de su creación hasta que llame a CArchive::Close.Este método puede llamar explícitamente por el programador o implícitamente por el destructor cuando el programa sale del ámbito que contiene el CArchive.

Esta nota describe la implementación de la CArchive miembros CArchive::ReadObject y CArchive::WriteObject.Encontrará el código para estas funciones en Arcobj.cpp y la implementación principal para CArchive en Arccore.cpp.Código de usuario no llama a ReadObject y WriteObject directamente.En su lugar, se utilizan estos objetos específicos de la clase los tipos de inserción y extracción de operadores que se generan automáticamente por el DECLARE_SERIAL y IMPLEMENT_SERIAL las macros.El siguiente código se muestra cómo WriteObject y ReadObject se llama de forma implícita:

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))

Guardar objetos en el almacén (CArchive::WriteObject)

El método CArchive::WriteObject escribe los datos de encabezado que se utilizan para reconstruir el objeto.Estos datos se compone de dos partes: el tipo del objeto y el estado del objeto.Este método también es responsable de mantener la identidad del objeto que se está escribiendo, por lo que se guarda una sola copia, independientemente del número de punteros a ese objeto (incluyendo los punteros circulares).

Guardar (Insertar) y la restauración de objetos (extracción) se basa en varios "constantes del manifiesto." Estos son los valores que se almacenan en formato binario y proporcionan información importante para el archivo (tenga en cuenta el prefijo "w" indica las cantidades de 16 bits):

Tag

Descripción

wNullTag

Puede utilizar para punteros de objeto NULL (0).

wNewClassTag

Indica la descripción de la clase que sigue es nuevo en este contexto de archivo (-1).

wOldClassTag

Indica la clase del objeto que se está leyendo se ha visto en este contexto (0 x 8000).

Cuando almacene objetos, el archivo mantiene un CMapPtrToPtr (el m_pStoreMap) que es una asignación de un objeto almacenado a un identificador persistente (PID) de 32 bits.Un PID se asigna a cada objeto único y todos los nombres de clase única que se guardan en el contexto del contenedor.Estos PID se distribuyen secuencialmente, comenzando en 1.Estos PID no tienen importancia fuera del ámbito del archivo y, en particular, no son debe ser confundido con números de registro u otros elementos de identidad.

En el CArchive (clase), PID son de 32 bits, pero se escriben como 16 bits a menos que sea mayor que 0x7FFE.PID de gran tamaño se escriben como 0x7FFF seguido por el PID de 32 bits.Esto mantiene la compatibilidad con proyectos creados en versiones anteriores.

Cuando se realiza una solicitud para guardar un objeto en un archivo (normalmente mediante el operador de inserción global), se realiza una comprobación para un valor NULL CObject puntero.Si el puntero es NULL, el wNullTag se insertan en la secuencia de archivo.

Si el puntero no es NULL y se pueden serializar (la clase es un DECLARE_SERIAL clase), el código comprueba la m_pStoreMap para ver si ya se guardó el objeto.Si es así, el código inserta el PID de 32 bits asociado a ese objeto en la secuencia de archivo.

Si el objeto no se ha guardado antes, existen dos posibilidades para tener en cuenta: el objeto y el tipo exacto (es decir, clase) del objeto son nuevos en este contexto de archivo o el objeto es de un tipo exacto que ya se ha visto.Para determinar si el tipo se ha visto, las consultas del código del m_pStoreMap para un CRuntimeClass objeto que coincide con el CRuntimeClass objeto asociado con el objeto que se va a guardar.Si hay una coincidencia, WriteObject inserta una etiqueta es el bit a bit OR de wOldClassTag y este índice.Si el CRuntimeClass es nuevo en este contexto de archivo, WriteObject asigna un nuevo PID en esa clase y lo inserta en el archivo, precedido por el wNewClassTag valor.

El descriptor de esta clase, a continuación, se inserta en el archivo con el CRuntimeClass::Store método.CRuntimeClass::StoreInserta el número de esquema de la clase (vea abajo) y el nombre de texto ASCII de la clase.Tenga en cuenta que el uso del nombre de texto ASCII no garantiza la unicidad del archivo a través de las aplicaciones.Por lo tanto, debe etiquetar los archivos de datos para evitar la corrupción.Tras la inserción de la información de clase, el archivo coloca el objeto en el m_pStoreMap y, a continuación, llama a la Serialize método para insertar datos específicos de la clase.Colocar el objeto en el m_pStoreMap antes de llamar a Serialize impide que se guarden en el almacén de varias copias del objeto.

Cuando se devuelve al llamador inicial (normalmente, la raíz de la red de objetos), se debe llamar a CArchive::Close.Si va a realizar otra CFilelas operaciones, se debe llamar a la CArchive método Flush para evitar la corrupción del archivo.

[!NOTA]

Esta implementación impone un límite físico de los índices de 0x3FFFFFFE por el contexto de archivo.Este número representa el número máximo de objetos únicos y las clases que se pueden guardar en un solo archivo, pero un solo archivo de disco puede tener un número ilimitado de contextos de contenedor.

Cargar objetos desde el almacén (CArchive::ReadObject)

Cargar (extracción) de objetos utiliza el CArchive::ReadObject método y es el inverso de WriteObject.Al igual que con WriteObject, ReadObject no llama directamente al código de usuario; el código de usuario debe llamar el operador de extracción de los tipos que llama a ReadObject con el esperado CRuntimeClass.Esto asegura la integridad del tipo de la operación de extracción.

Dado que la WriteObject aplicación asignado PID crecientes, comenzando por 1 (0 está predefinido como el objeto nulo), el ReadObject aplicación puede utilizar una matriz para mantener el estado del contexto de archivo.Cuando se lee un PID de la tienda, si el PID es mayor que el actual límite superior de la m_pLoadArray, ReadObject sabe que sigue un nuevo objeto (o la descripción de la clase).

Números de esquema

El número de esquema, que se asigna a la clase cuando el IMPLEMENT_SERIAL método de la clase se encuentra, es la "versión" de la implementación de la clase.El esquema hace referencia a la implementación de la clase, no al número de veces que un objeto determinado se ha persistente (que normalmente se denomina la versión del objeto).

Si tiene la intención de mantener varias implementaciones diferentes de la misma clase con el tiempo, incrementar el esquema como revisión del objeto Serialize la implementación del método permiten escribir código que puede cargar los objetos almacenados mediante el uso de las versiones anteriores de la aplicación.

El CArchive::ReadObject método producirá una CArchiveException cuando encuentre un número de esquema en el almacén persistente que difiere del número de esquema de la descripción de la clase en la memoria.No es fácil recuperarse de esta excepción.

Puede utilizar VERSIONABLE_SCHEMA combinado con (bit a bit OR) su versión de esquema que se inicie esta excepción.Mediante el uso de VERSIONABLE_SCHEMA, el código puede realizar la acción apropiada su Serialize función comprobando el valor devuelto por CArchive::GetObjectSchema.

Llamada serializar directamente

En muchos casos, la sobrecarga del sistema de archivo objeto general de WriteObject y ReadObject no es necesario.Este es el caso habitual de serializar los datos en un CDocument.En este caso, el Serialize método de la CDocument se llama directamente, no con los operadores de extracción o inserción.El contenido del documento a su vez puede utilizar el esquema de archivo de objeto más general.

Llamar a Serialize directamente tiene las siguientes ventajas e inconvenientes:

  • No hay bytes adicionales se agregan al archivo antes o después de que se serializa el objeto.Esto no sólo hace que los datos guardados más pequeños, pero permite implementar Serialize formatos de archivo de rutinas que pueden manejar cualquiera.

  • El MFC está ajustado para que el WriteObject y ReadObject las implementaciones y colecciones relacionadas no estará vinculadas a la aplicación a menos que necesite el sistema de archivo de objeto más general para otros propósitos.

  • El código no tiene para recuperarse de los números de esquema anterior.Esto hace que el código de serialización documento responsable de la codificación de los números de esquema, los números de versión de formato de archivo o cualquier identificación de los números que utilice en el inicio de los archivos de datos.

  • Cualquier objeto que se serializa con una llamada directa a Serialize no debe usar CArchive::GetObjectSchema o que debe identificador de un valor devuelto (UINT) -1 indica que la versión era desconocida.

Debido a que Serialize se llama directamente en el documento, no es normalmente posible para los objetos secundarios del documento para archivar las referencias a su documento padre.Estos objetos deben proporcionarse un puntero a su documento contenedor explícitamente o se debe utilizar CArchive::MapObject función que asigna el CDocument puntero a un PID antes de que se archiven estos punteros posteriores.

Como se indicó anteriormente, debe codificar la versión y clase información cuando se llama a Serialize directamente, lo que permite cambiar el formato más adelante mientras mantiene la compatibilidad con versiones anteriores con los archivos más antiguos.El CArchive::SerializeClass función se puede llamar explícitamente antes de serializar directamente un objeto o antes de llamar a una clase base.

Vea también

Otros recursos

Notas técnicas por número

Notas técnicas por categoría