Condividi tramite


TN002: Formato persistente dati dell'oggetto

Questa nota vengono descritte le routine MFC che supportano oggetti persistenti di C++ e il formato dei dati degli oggetti memorizzati in un file.Questo vale solo per le classi con la DECLARE_SERIAL e IMPLEMENT_SERIAL delle macro.

Il problema

L'implementazione di MFC per i dati persistenti memorizza dati per molti oggetti in una singola parte contigua di un file.L'oggetto Serialize metodo converte i dati dell'oggetto in un formato binario compatto.

L'implementazione garantisce che tutti i dati vengono salvati nello stesso formato utilizzando il Classe CArchive.Utilizza un CArchive oggetto di un traduttore.Tale oggetto persiste dal momento in cui viene creato finché non si chiama CArchive::Close.Questo metodo può essere chiamato in modo esplicito dal programmatore o in modo implicito dal distruttore quando il programma si chiude l'ambito che contiene la CArchive.

Questa nota viene descritta l'implementazione del CArchive i membri CArchive::ReadObject e CArchive::WriteObject.Si noterà il codice per queste funzioni in Arcobj.cpp e l'implementazione principale per CArchive in Arccore.cpp.Non chiamare il codice utente ReadObject e WriteObject direttamente.Questi oggetti vengono invece utilizzati dagli specifici della classe type-safe inserimento ed estrazione operatori che vengono generati automaticamente dal DECLARE_SERIAL e IMPLEMENT_SERIAL delle macro.Il seguente codice viene illustrato come WriteObject e ReadObject vengono chiamati in modo implicito:

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

Salvataggio di oggetti nell'archivio (CArchive::WriteObject)

Il metodo CArchive::WriteObject scrive i dati di intestazione viene utilizzati per ricostruire l'oggetto.Questi dati è costituito da due parti: il tipo di oggetto e lo stato dell'oggetto.Questo metodo è anche responsabile della gestione dell'identità dell'oggetto verrà scritta, in modo che una sola copia viene salvata, indipendentemente dal numero di puntatori a quell'oggetto (inclusi riferimenti circolari).

Salvataggio (inserimento) e il ripristino degli oggetti (estrazione) si basa su diversi "costanti manifesto." Questi sono i valori che vengono memorizzati in formato binario e forniscono informazioni importanti per l'archivio (notare il prefisso "w" indica la quantità a 16 bit):

Tag

Descrizione

wNullTag

Utilizzato per puntatori a oggetti di valore NULL (0).

wNewClassTag

Indica la descrizione della classe che segue è una novità di questo contesto di archivio (-1).

wOldClassTag

Indica la classe dell'oggetto letto finora in questo contesto (0x8000).

Quando si archiviano gli oggetti, l'archivio gestisce un CMapPtrToPtr (il m_pStoreMap) che è un mapping da un oggetto memorizzato in un identificatore persistente a 32 bit (PID).Un PID viene assegnato a ogni oggetto univoco e ogni nome di classe univoco che viene salvato nel contesto dell'archivio.Sono questi PID presentati in sequenza a partire da 1.I PID non hanno alcun significato all'esterno dell'ambito dell'archivio e sono in particolare, non devono essere confusi con i numeri di record o altri elementi di identità.

Nel CArchive classe, i PID sono a 32 bit, ma essi vengono scritti come a 16 bit a meno che non siano superiori a 0x7FFE.PID di grandi dimensioni vengono scritti come 0x7FFF seguita dal PID a 32 bit.Questo consente di mantenere la compatibilità con i progetti che sono stati creati in versioni precedenti.

Quando viene effettuata una richiesta di salvare un oggetto in un archivio (in genere, utilizzando l'operatore di inserimento globale), viene effettuata una verifica per un valore NULL CObject puntatore.Se il puntatore è NULL, la wNullTag viene inserito nel flusso dell'archivio.

Se il puntatore del mouse non è NULL e possono essere serializzati (la classe è un DECLARE_SERIAL classe), viene verificato il m_pStoreMap per verificare se l'oggetto è stato già salvato.In caso affermativo, il codice inserisce il numero di serie a 32 bit associato a tale oggetto nel flusso dell'archivio.

Se l'oggetto non è stato salvato prima, esistono due possibilità da prendere in considerazione: l'oggetto e il tipo esatto (vale a dire classe) dell'oggetto sono nuove per il contesto di archivio oppure l'oggetto è di un tipo esatto già visualizzato.Per determinare se il tipo è stato osservato, le query di codice il m_pStoreMap per un CRuntimeClass oggetto corrisponde il CRuntimeClass oggetto associato all'oggetto da salvare.Se viene trovata una corrispondenza, WriteObject inserisce un tag che è il bit per bit OR di wOldClassTag e l'indice.Se il CRuntimeClass è una novità di questo contesto di archivio WriteObject assegna un nuovo PID per tale classe e lo inserisce nell'archivio, preceduto dalla wNewClassTag valore.

Il descrittore di questa classe viene quindi inserito nella utilizzando archivio di CRuntimeClass::Store metodo.CRuntimeClass::StoreInserisce il numero di schema della classe (vedere sotto) e il nome di testo ASCII della classe.Si noti che l'utilizzo del nome di testo ASCII non garantisce l'univocità dell'archivio tra le applicazioni.Di conseguenza, si consiglia di etichettare i file di dati per evitare danni.Dopo l'inserimento delle informazioni di classe, l'archivio viene inserito l'oggetto di m_pStoreMap , quindi chiama il Serialize metodo per inserire i dati specifici di classe.Inserire l'oggetto di m_pStoreMap prima di chiamare Serialize impedisce di salvare nell'archivio di più copie dell'oggetto.

Quando viene restituita al chiamante iniziale (in genere la directory principale della rete di oggetti), è necessario chiamare CArchive::Close.Se si prevede di eseguire altri CFilele operazioni, è necessario chiamare il CArchive metodo Flush per evitare il danneggiamento dell'archivio.

[!NOTA]

Questa implementazione impone un limite massimo di indici di 0x3FFFFFFE per ogni contesto di archivio.Questo numero rappresenta il numero massimo di oggetti univoci e le classi che possono essere salvate in un unico archivio, ma un singolo file su disco può avere un numero illimitato di contesti di archivio.

Caricamento degli oggetti dall'archivio (CArchive::ReadObject)

Durante il caricamento (estrazione) degli oggetti viene utilizzato il CArchive::ReadObject metodo ed è il contrario di WriteObject.Come con WriteObject, ReadObject non viene chiamato direttamente dal codice utente; l'operatore di estrazione di type-safe che chiama il codice utente deve chiamare ReadObject con la prevista CRuntimeClass.In questo modo l'integrità del tipo dell'operazione di estrazione.

Poiché il WriteObject implementazione assegnato PID crescente, a partire da 1 (0 predefinito dell'oggetto NULL), il ReadObject implementazione può utilizzare una matrice per mantenere lo stato del contesto dell'archivio.Quando un PID viene letto dall'archivio, se il numero di serie è maggiore del limite superiore corrente del m_pLoadArray, ReadObject sa che segue un nuovo oggetto (o la descrizione della classe).

Numeri di schema

Il numero di schema, che viene assegnato alla classe quando il IMPLEMENT_SERIAL metodo della classe viene rilevato, è la "versione" di implementazione della classe.Lo schema si riferisce all'implementazione della classe, non per il numero di volte in cui un determinato oggetto reso permanente (in genere definito la versione dell'oggetto).

Se si intende gestire diverse implementazioni della stessa classe nel tempo, incrementare lo schema durante la revisione dell'oggetto Serialize implementazione del metodo consentirà di scrivere codice in grado di caricare gli oggetti archiviati utilizzando le versioni precedenti dell'implementazione.

Il CArchive::ReadObject metodo genererà un eccezione CArchiveException quando incontra un numero di schema dell'archivio permanente che è diverso dal numero di schema della descrizione della classe in memoria.Non è facile per il ripristino da questa eccezione.

È possibile utilizzare VERSIONABLE_SCHEMA combinato con (OR bit per bit OR) la versione dello schema per mantenere questa eccezione viene generata un'eccezione.Utilizzando VERSIONABLE_SCHEMA, il codice può eseguire l'azione appropriata sua Serialize funzione controllando il valore restituito da CArchive::GetObjectSchema.

Chiamata serializzare direttamente

In molti casi, l'overhead del regime generale oggetto archivio di WriteObject e ReadObject non è necessario.Questo è il caso comune di serializzazione dei dati in un CDocument.In questo caso, il Serialize metodo del CDocument viene chiamato direttamente, non con gli operatori di estrazione o insert.Il contenuto del documento a sua volta può utilizzare lo schema di archivio di oggetti più generale.

Chiamata di Serialize direttamente con i seguenti vantaggi e svantaggi:

  • Nessun byte aggiuntivi vengono aggiunti all'archivio prima o dopo l'oggetto viene serializzato.Questo non solo rende i dati salvati più piccoli, ma consente di implementare Serialize routine in grado di gestire i formati di file.

  • Il dispositivo MFC sia regolata in modo che WriteObject e ReadObject implementazioni e agli insiemi correlati non verranno collegati nell'applicazione, a meno che lo schema di archivio di oggetti più generale è necessario per altri scopi.

  • Non è il codice per il ripristino da numeri di schema precedenti.In questo modo responsabile per i numeri dello schema di codifica, numeri di versione del formato di file o qualsiasi che identifica i numeri del codice di serializzazione del documento utilizzare all'inizio del file di dati.

  • Qualsiasi oggetto serializzato con una chiamata diretta a Serialize non deve utilizzare CArchive::GetObjectSchema o che deve handle, il valore restituito (UINT) -1 indica che la versione è sconosciuta.

Poiché Serialize viene chiamato direttamente nel documento, non è in genere possibile di oggetti secondari del documento per archiviare i riferimenti al documento principale.Questi oggetti devono essere forniti un puntatore a loro il documento contenitore in modo esplicito oppure è necessario utilizzare CArchive::MapObject funzione per eseguire il mapping di CDocument puntatore a un PID prima che questi puntatori posteriori vengono archiviati.

Come accennato in precedenza, è necessario codificare la versione e informazioni sulla classe manualmente quando si chiama Serialize direttamente, consentendo di modificare il formato in un secondo momento, mantenendo al tempo stesso garantire la compatibilità con versioni precedenti.Il CArchive::SerializeClass funzione può essere chiamato in modo esplicito prima della serializzazione di un oggetto direttamente o prima di chiamare una classe base.

Vedere anche

Altre risorse

Note tecniche del numero

Note tecniche per categoria