Partager via


TN002 : format de données persistant d'objet

Cette note décrit les routines MFC qui prennent en charge les objets C++ persistants et le format des données d'objet lorsqu'elle est stockée dans un fichier.Cela s'applique uniquement aux classes avec la DECLARE_SERIAL et IMPLEMENT_SERIAL les macros.

Le problème

L'implémentation MFC pour les données persistantes stocke les données pour de nombreux objets dans une seule partie contiguë d'un fichier.L'objet Serialize méthode traduit les données de l'objet en un format binaire compact.

La mise en œuvre garantit que toutes les données sont enregistrées dans le même format à l'aide de la CArchive, classe.Il utilise un CArchive objet en tant que traducteur.Cet objet persiste entre le moment où il est créé jusqu'à ce que vous appeliez CArchive::Close.Cette méthode peut être appelée explicitement par le programmeur ou implicitement par le destructeur lorsque le programme quitte la portée contenant la CArchive.

Cette note décrit l'implémentation de la CArchive membres CArchive::ReadObject et CArchive::WriteObject.Vous y trouverez le code pour ces fonctions dans Arcobj.cpp et l'implémentation principale pour CArchive dans Arccore.cpp.Code utilisateur n'appelle pas ReadObject et WriteObject directement.Au lieu de cela, ces objets sont utilisés par les opérateurs de d'insertion et d'extraction de type sécurisé spécifiques à la classe qui sont générés automatiquement par le DECLARE_SERIAL et IMPLEMENT_SERIAL les macros.Le code suivant montre comment WriteObject et ReadObject sont appelés implicitement :

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

L'enregistrement des objets dans le magasin (CArchive::WriteObject)

La méthode CArchive::WriteObject écrit les données d'en-tête qui sont utilisées pour reconstruire l'objet.Ces données se composent de deux parties : le type de l'objet et l'état de l'objet.Cette méthode est également responsable du maintien de l'identité de l'objet en cours d'écriture, afin que seule une copie est enregistrée, quel que soit le nombre de pointeurs vers cet objet (y compris les pointeurs circulaires).

L'enregistrement (Insertion) et restaurer des objets (extraction) s'appuie sur plusieurs « constantes manifeste ». Il s'agit des valeurs qui sont stockées sous forme binaire et fournissent des informations importantes pour l'archivage (Notez le préfixe « w » indique les quantités de 16 bits) :

Tag

Description

wNullTag

Utilisé pour des pointeurs d'objet NULL (0).

wNewClassTag

Indique la description de la classe qui suit est une nouveauté dans ce contexte d'archive (-1).

wOldClassTag

Indique la classe de l'objet en cours de lecture a été vu dans ce contexte (0 x 8000).

Lorsque vous stockez des objets, l'archive conserve une CMapPtrToPtr (le m_pStoreMap) qui est un mappage à partir d'un objet stocké à un identificateur persistant de 32 bits (PID).Un PID est affecté à chaque objet unique et chaque nom de classe unique qui est enregistré dans le contexte de l'archive.Ces PIDs sont distribuées séquentiellement, en commençant à 1.Ces PIDs n'ont aucune signification en dehors de la portée de l'archive et, en particulier, sont à ne pas confondre avec les numéros d'enregistrement ou d'autres éléments de l'identité.

Dans la CArchive (classe), PID est 32 bits, mais ils sont écrits en 16 bits, sauf si elles sont supérieures à 0x7FFE.Grands PIDs sont écrites sous 0x7FFF suivie par le PID de 32 bits.Ceci assure la compatibilité avec les projets qui ont été créés dans les versions antérieures.

Lorsqu'une demande est faite pour enregistrer un objet dans une archive (généralement en utilisant l'opérateur d'insertion global), une vérification est effectuée pour une valeur NULL CObject pointeur.Si le pointeur est NULL, la wNullTag est insérée dans le flux de données d'archive.

Si le pointeur n'est pas NULL et peuvent être sérialisé (la classe est un DECLARE_SERIAL classe), les contrôles de code la m_pStoreMap pour voir si l'objet a déjà été enregistré.Si elle a, le code insère le PID de 32 bits associé à cet objet dans le flux de l'archive.

Si l'objet n'a pas été enregistré, il existe deux possibilités pour prendre en compte : l'objet et le type exact (c'est-à-dire, classe) de l'objet sont nouvelles pour ce contexte d'archivage, soit l'objet est d'un type exact de déjà vu.Pour déterminer si le type a été constaté, les requêtes de code la m_pStoreMap pour un CRuntimeClass objet qui correspond à la CRuntimeClass objet associé à l'objet enregistré.S'il existe une correspondance, WriteObject insère une balise qui est le bit OR de wOldClassTag et cet index.Si la CRuntimeClass est une nouveauté dans ce contexte d'archivage, WriteObject attribue un nouveau PID à cette classe et l'insère dans l'archive, précédé par le wNewClassTag valeur.

Le descripteur de cette classe est ensuite inséré dans l'archive à l'aide de la CRuntimeClass::Store méthode.CRuntimeClass::StoreInsère le numéro de schéma de la classe (voir ci-dessous) et le nom de texte ASCII de la classe.Notez que l'utilisation du nom de texte ASCII ne garantit pas l'unicité de l'archivage entre applications.Par conséquent, il doit baliser vos fichiers de données pour éviter la corruption.La suite de l'insertion des informations des classe, l'archive place l'objet dans le m_pStoreMap , puis appelle la Serialize méthode pour insérer des données spécifiques à la classe.Placer l'objet dans le m_pStoreMap avant d'appeler Serialize empêche que plusieurs copies de l'objet en cours d'enregistrement à la banque.

Lors du retour à l'appelant initial (généralement la racine du réseau d'objets), vous devez appeler CArchive::Close.Si vous prévoyez d'effectuer d'autres CFileopérations, vous devez appeler le CArchive méthode Flush pour éviter la corruption de l'archive.

[!REMARQUE]

Cette implémentation impose une limite stricte d'indices de 0x3FFFFFFE par le contexte de l'archive.Ce nombre représente le nombre maximal d'objets uniques et des classes qui peuvent être enregistrées dans une archive unique, mais un fichier de disque unique peut avoir un nombre illimité de contextes d'archive.

Chargement d'objets à partir de la banque (CArchive::ReadObject)

Chargement (extraction) des objets utilise le CArchive::ReadObject méthode et est l'inverse de WriteObject.Comme avec WriteObject, ReadObject n'est pas appelée directement par le code utilisateur ; l'opérateur d'extraction de type sécurisé qui appelle le code utilisateur doit appeler ReadObject avec le texte attendu CRuntimeClass.Cela garantit l'intégrité du type de l'opération d'extraction.

Dans la mesure où le WriteObject implémentation affecté PIDs croissants, en commençant par 1 (0 est prédéfini comme objet NULL), la ReadObject implémentation peut utiliser un tableau pour maintenir l'état du contexte de l'archive.Quand un PID est lu à partir du magasin, si le PID est supérieur à la limite supérieure en cours de la m_pLoadArray, ReadObject sait qu'un nouvel objet (ou la description de la classe) suit.

Numéros de schéma

Le numéro de schéma, qui est affecté à la classe lors de la IMPLEMENT_SERIAL méthode de la classe est rencontrée, est la « version » de l'implémentation de classe.Le schéma fait référence à l'implémentation de la classe, pas le nombre de fois où un objet donné a été apportée permanent (généralement dénommée la version de l'objet).

Si vous avez l'intention de maintenir plusieurs implémentations différentes de la même classe dans le temps, l'incrémentation du schéma que vous révisez votre objet Serialize implémentation de la méthode vous permettra d'écrire du code qui peut charger des objets stockés à l'aide d'anciennes versions de la mise en oeuvre.

Le CArchive::ReadObject méthode lèvera une exception CArchiveException lorsqu'il rencontre un numéro de schéma dans le magasin persistant qui diffère du nombre de schéma de la description de classe en mémoire.Il n'est pas facile de récupérer à partir de cette exception.

Vous pouvez utiliser VERSIONABLE_SCHEMA combiné avec (au niveau du bit OR) votre version de schéma pour conserver cette levée d'exception.À l'aide de VERSIONABLE_SCHEMA, votre code peut prendre les mesures appropriées sa Serialize fonction en vérifiant la valeur de retour de CArchive::GetObjectSchema.

Appel sérialiser directement

Dans de nombreux cas, la charge du régime général objet archive de WriteObject et ReadObject n'est pas nécessaire.C'est le cas commun de sérialiser les données dans un CDocument.Dans ce cas, la Serialize méthode de le CDocument est appelé directement, pas avec les opérateurs d'extraction ou insertion.Le contenu du document peut-être à leur tour utilisent le jeu d'archive objet plus général.

L'appel de Serialize directement sont les avantages et inconvénients suivants :

  • Aucun octets supplémentaires ne sont ajoutés à l'archive avant ou après que l'objet est sérialisé.Cela réduit la taille des données enregistrées, non seulement mais vous permet d'implémenter Serialize routines qui peuvent prendre en charge les formats de fichier.

  • Le MFC est réglé afin que la WriteObject et ReadObject mises en œuvre et les collections associées ne seront pas liées dans votre application, sauf si vous devez le schéma d'archive objet plus général à d'autres fins.

  • Votre code ne doit pas récupérer d'anciens numéros du schéma.Cela rend votre code de sérialisation de document responsable du codage schéma nombres, des numéros de version de format de fichier ou toute identification des nombres utiliser au début de vos fichiers de données.

  • Tout objet qui est sérialisé avec un appel direct à Serialize ne doit pas utiliser CArchive::GetObjectSchema ou devez handle d'une valeur de retour de -1 (UINT) indiquant que la version était inconnue.

Car Serialize est appelée directement sur votre document, il est généralement pas possible pour les sous-objets du document à archiver les références à leur document parent.Ces objets doivent être reçoit un pointeur désignant son document conteneur explicitement, ou vous devez utiliser CArchive::MapObject fonction permettant de mapper le CDocument pointeur vers un PID avant ces pointeurs arrière sont archivés.

Comme indiqué précédemment, vous devez coder la version et informations sur les classes vous-même lorsque vous appelez Serialize directement, ce qui vous permet de modifier ultérieurement le format tout en conservant une compatibilité ascendante avec les anciens fichiers.Le CArchive::SerializeClass fonction peut être appelée explicitement avant la sérialisation d'un objet ou avant d'appeler une classe de base.

Voir aussi

Autres ressources

Notes techniques de nombres

Notes techniques de catégorie