Esta nota describe las rutinas MFC que admiten objetos persistentes de C++ y el formato de los datos de objeto cuando se almacena en un archivo. Esto sólo se aplica a las clases con las macros DECLARE_SERIAL y IMPLEMENT_SERIAL.
El problema
La aplicación MFC datos persistentes se basa en un formato binario compacto para guardar los datos de muchos objetos en una sola parte contigua de un archivo. Este formato binario proporciona la estructura de cómo se almacenan los datos, pero es la función del objeto Serialize miembros que proporciona los datos reales que guarda el objeto.
El MFC solucionó el problema de estructuración mediante la clase CArchive. Un objeto CArchive proporciona un contexto de persistencia que dura desde el momento en que el archivo se crea hasta que se llama la función de miembro de CArchive::Close , ya sea explícitamente por el programador, o implícitamente por el destructor cuando se sale el alcance que contiene el CArchive.
Esta nota describe la implementación de los miembros de CArchive ReadObject y WriteObject. ReadObject y WriteObject no llama directamente por el contrario son utilizados por operadores de inserción y extracción de la seguridad de tipos de específicos de la clase generados automáticamente por las macros DECLARE_SERIAL y IMPLEMENT_SERIAL.
clase CMyObject: CObject pública
{
nbsp; DECLARE_SERIAL(CMyObject)
};
IMPLEMENT_SERIAL (CMyObj, CObject, 1)
/ / ejemplo de uso (ar es un CArchive &)
CMyObject * pObj;
CArchive & ar;
ar << pObj; / / llama ar.WriteObject(pObj)
ar >> pObj; / / llama ar.ReadObject(RUNTIME_CLASS(CObj))
Esta nota describe el código que se encuentra en el archivo de código fuente MFC ARCOBJ.CPP. La principal aplicación de CArchive puede encontrarse en ARCCORE.CPP.
Guardar objetos en la tienda (CArchive::WriteObject)
La función miembro CArchive::WriteObject escribe los datos de encabezado utilizados para reconstruir el objeto. Estos datos se compone de dos partes: el tipo de objeto y el estado del objeto. Esta función miembro es también responsable de mantener la identidad del objeto escrita, por lo que se guarda una sola copia, independientemente del número de punteros a dicho objeto (incluyendo punteros circulares).
Guardar (insertar) y restaurar objetos (extracción) se basa en varias "manifiestos constantes". Estos son valores que se almacenan en formato binario y proporcionan información importante para el archivo (nota el prefijo "w" indica cantidades de 16 bits):
| Etiqueta | Descripción |
| wNullTag | Utilizado para los punteros de objeto nulo (0). |
| wNewClassTag | Indica la descripción de la clase que sigue es nuevo en este context—(-1) de archivo. |
| wOldClassTag | Indica la clase del objeto que se está leyendo se ha visto en este contexto (0 x 8000). |
Al almacenar objetos, el archivo mantiene un CMapPtrToPtr ( m_pStoreMap) que es una asignación de un objeto almacenado en un identificador persistente de 32 bits (PID). Un PID se asigna a cada objeto único y cada nombre de clase única que se guarda en el contexto del contenedor. Estos PIDs se distribuyen secuencialmente comenzando por 1. Es importante señalar que estos PIDs no tengan ningún significado fuera del ámbito del archivo y, en particular, no son debe ser confundido con número de registro u otros elementos de identidad.
Comenzando con la versión 4.0 de MFC la clase CArchive se ha ampliado para soportar archivos muy grandes. En versiones anteriores, un PID fue una cantidad de 16 bits, limitando el archivo 0x7FFE (32766) objetos. PID ahora son de 32 bits, pero se escriben como de 16 bits a menos que sean mayores que 0x7FFE. Grandes PIDs se escriben como 0x7FFF seguido por el PID de 32 bits. Esta técnica mantiene la compatibilidad con versiones anteriores de archivos.
Cuando se realiza una solicitud para guardar un objeto en un contenedor (normalmente mediante el operador de inserción global), se realiza una comprobación para un puntero NULL CObject ; Si el puntero es NULL, el wNullTag se inserta en la secuencia de archivo.
Si tenemos un puntero de objeto real que es capaz de ser serializado (la clase es una clase DECLARE_SERIAL ), luego comprobamos la m_pStoreMap para ver si ya se ha guardado el objeto. Si tiene, insertamos el PID de 32-bit asociado con ese objeto.
Si el objeto no se ha guardado antes, hay dos posibilidades, debemos tener en cuenta: el objeto y el tipo exacto (es decir, la clase) del objeto son nuevos en este contexto de archivo o el objeto es de un tipo exacto ya visto. Para determinar si el tipo se ha visto que consulta la m_pStoreMap de un objeto CRuntimeClass que coincide con el objeto de CRuntimeClass asociado con el objeto que estamos ahorrando. Si hemos visto esta clase antes, WriteObject inserta una etiqueta que es el OR'ing bit a bit de wOldClassTag y este índice. Si el CRuntimeClass es nuevo en este contexto de archivo, entonces WriteObject asigna un PID nuevo a esa clase e insertarlo en el archivo, precedido por el valor de wNewClassTag.
El descriptor de esta clase, a continuación, se inserta en el archivo utilizando la función de miembro de CRuntimeClass tienda. CRuntimeClass::Store inserta el número de esquema de la clase (véase infra) 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 es recomendable etiquetar los archivos de datos para prevenir la corrupción. Tras la inserción de la información de la clase, el archivo coloca el objeto en la m_pStoreMap y, a continuación, llama a la función de miembro Serialize para insertar datos específicos de la clase en el archivo. Colocar el objeto en el m_pStoreMap antes de llamar a Serialize impide a varias copias del objeto se guarda en el almacén.
Al regresar a la llamada inicial (generalmente la raíz de la red de objetos), es importante Cerrar el archivo. Si otras operaciones CFile va a hacerse, debe llamarse la función de miembro CArchive Flush . Si no lo hace tendrá como resultado un archivo dañado.
&Notanbsp; Esta implementación impone un límite de 0x3FFFFFFE índices por contexto de archivo. Este número representa el número máximo de objetos únicos y las clases que se pueden guardar en un archivo único, pero nota que un único disco archivo puede tener un número ilimitado de contextos de archivo.
Carga de objetos de la tienda (CArchive::ReadObject)
Carga de objetos (extracción) utiliza la función de miembro de CArchive::ReadObject y es el inverso de WriteObject. Como con WriteObject, ReadObject no llama directamente al código de usuario; código de usuario debe llamar al operador de extracción de tipo seguro que llama ReadObject con el esperado CRuntimeClass. Esto asegura la integridad del tipo de la operación de extracción.
Desde la aplicación WriteObject asignado crecientes PIDs, comenzando con 1 (0 es predefinido como el objeto NULL), la aplicación de ReadObject 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, luego ReadObject sabe que sigue a un nuevo objeto (o descripción de clase).
Números de esquema
El número de esquema, que se asigna a la clase cuando se encuentra IMPLEMENT_SERIAL la clase , es la "versión" de la implementación de la clase. El esquema se refiere a la implementación de la clase, no al número de veces que un determinado objeto realizado persistente (generalmente denominado como la versión del objeto).
Si desea ma&ntener varias implementaciones diferentes de la misma clase con el tiempo, incrementando el esquema como revisión de implementación de función de miembro del objeto Serialize le permitirá escribir código que puede cargar objetos almacenados utilizando versiones anteriores de la implementation.nbsp;
La función de miembro de CArchive::ReadObject generará una CArchiveException cuando encuentra un número de esquema en el almacén persistente que difiere el número de esquema de la descripción de clase en la memoria. No es fácil recuperarse de esta excepción.
Puede utilizar VERSIONABLE_SCHEMA O tuvo con su versión de esquema para mantener esta excepción se produce. Mediante el uso de VERSIONABLE_SCHEMA, el código puede tomar la acción apropiada en su función Serialize comprobando el valor devuelto por CArchive::GetObjectSchema.
Llamar a serializar directamente
Hay muchos casos donde la sobrecarga del sistema de archivo general objeto de WriteObject y ReadObject no es necesario o deseado. Este es el caso común de serializar los datos en un CDocument. En este caso la función de miembro Serialize de la CDocument se llama directamente, no con el extracto o insertar operadores. El contenido del documento a su vez puede utilizar el sistema de archivo de objeto más general.
Llamar directamente a Serialize tiene las siguientes ventajas y desventajas:
Porque Serialize se llama directamente en el documento, no es normalmente posible subobjetos del documento para archivar las referencias al documento de su padre. Estos objetos deben tener un puntero a su documento contenedor explícitamente o debe utilizar la función de CArchive::MapObject para asignar el puntero CDocument a un PID antes estos punteros atrás están archivados.
Como se señaló anteriormente, debe codificar la información de versión y clase usted al llamar Serialize directamente, lo que le permite cambiar el formato más tarde mientras mantiene la compatibilidad con archivos antiguos. La función CArchive::SerializeClassRef puede llamarse explícitamente antes directamente serializar un objeto o antes de llamar a una clase base.
&Notas técnicas por número |nbsp; Notas técnicas por categoría