TN002: Persistentes Objektdaten-Format
Diese Notiz beschreibt die MFC-Routinen, die permanente C++-Objekte und das Format der Daten des Objekts unterstützen, wenn sie in einer Datei gespeichert werden.Dies gilt nur für Klassen mit den DECLARE_SERIAL und IMPLEMENT_SERIAL Makros.
Das Problem
Die MFC-Implementierung für persistente Daten speichert Daten für viele Objekte in einem einzelnen zusammenhängenden Teil einer Datei.Des Objekts Serialize Methode übersetzt die Daten des Objekts in ein kompaktes binäres Format.
Die Implementierung wird sichergestellt, dass alle Daten werden im selben Format gespeichert, mit der CArchive-Klasse.Es verwendet ein CArchive Objekt als Übersetzer.Dieses Objekt bleibt bestehen, ab dem Zeitpunkt ihrer Erstellung bis zur rufen Sie CArchive::Close.Diese Methode kann aufgerufen werden durch den Programmierer explizit oder implizit durch den Destruktor beim Beenden des Programms über des Bereichs, der enthält die CArchive.
Diese Notiz beschreibt die Implementierung von der CArchive Elemente CArchive::ReadObject und CArchive::WriteObject.Finden Sie den Code für diese Funktionen in Arcobj.cpp und die wichtigsten Implementierung für CArchive in Arccore.cpp.Benutzercode nicht aufgerufen ReadObject und WriteObject direkt.Diese Objekte werden stattdessen durch klassenspezifische typsichere einfügen und Extraktion von Operatoren, die automatisch von der Serialisierungsinfrastruktur verwendet die DECLARE_SERIAL und IMPLEMENT_SERIAL Makros.Der folgende code zeigt, wie WriteObject und ReadObject werden implizit aufgerufen:
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))
Speichern von Objekten im Speicher (CArchive::WriteObject)
Die Methode CArchive::WriteObject schreibt der Headerdaten, die verwendet wird, um das Objekt zu rekonstruieren.Diese Daten besteht aus zwei Teilen: der Typ des Objekts und den Zustand des Objekts.Diese Methode ist auch verantwortlich für die Aufrechterhaltung der Identität des Objekts geschrieben wird, so dass nur eine einzige Kopie gespeichert werden, unabhängig von der Anzahl von Zeigern auf das Objekt (einschließlich kreisförmigen Zeiger).
(Einfügen) speichern und Wiederherstellen von Objekten (extrahierende) stützt sich auf mehrere "Manifestkonstanten." Dies sind die Werte, die in binär gespeichert und enthalten wichtige Informationen für das Archiv (Beachten Sie, dass das Präfix "w" 16-Bit-Mengen angibt):
Tag |
Beschreibung |
---|---|
wNullTag |
Für NULL-Objektzeigern (0) verwendet. |
wNewClassTag |
Zeigt an, dass der Beschreibung-Klasse, die folgt neu in diesem Zusammenhang Archiv (-1). |
wOldClassTag |
Gibt an, die Klasse des Objekts gelesen in diesem Kontext (0 x 8000) gesehen wurde. |
Wenn Sie Objekte zu speichern, verwaltet das Archiv eine CMapPtrToPtr (die m_pStoreMap) ist eine Zuordnung eines gespeicherten Objekts zu einem permanenten 32-Bit-ID (PID).Eine PID zugewiesen jedes Objekt eindeutig und jeden eindeutigen Klassennamen, die im Rahmen des Archivs gespeichert ist.Diese PIDs ausgehändigt sequenziell beginnend mit 1.Diese PIDs außerhalb des Bereichs des Archivs keine Bedeutung haben und insbesondere nicht zu verwechseln mit Datensatznummern oder andere Identität sind.
In der CArchive -Klasse, PIDs sind 32-Bit, aber sie werden ausgeschrieben als 16-Bit, wenn sie größer als 0x7FFE sind.Große PIDs werden als 0x7FFF, gefolgt von der 32-Bit-PID geschrieben.Dadurch bleibt die Kompatibilität mit Projekten, die in früheren Versionen erstellt wurden.
Wenn eine Anforderung erfolgt (in der Regel mithilfe des globalen Einfügeoperators) ein Objekt in ein Archiv zu speichern, wird überprüft für einen NULL-Wert CObject Zeiger.Wenn der Zeiger NULL ist, ist die wNullTag in den Archiv-Stream eingefügt.
Wenn der Zeiger nicht NULL ist und serialisiert werden können (die Klasse ist ein DECLARE_SERIAL Klasse), überprüft der Code die m_pStoreMap zu sehen, ob das Objekt bereits gespeichert wurde.Wenn Ja, fügt der Code die 32-Bit-PID mit diesem Objekt verknüpft ist, in den Archiv-Stream.
Wenn das Objekt noch nicht gespeichert wurde, gibt es zwei Möglichkeiten zu berücksichtigen: das Objekt und den genauen Typ (d. h. Klasse) des Objekts sind neu in diesem Zusammenhang Archiv oder das Objekt ist eines genauen Typs bereits gesehen.Um festzustellen, ob der Typ beobachtet, Code Abfragen der m_pStoreMap für ein CRuntimeClass -Objekt, das entspricht der CRuntimeClass Objekt gespeicherten Objekts zugeordnet.Wenn es eine Übereinstimmung, WriteObject Fügt ein Tag, das die bitweise OR von wOldClassTag und diesen Index.Wenn die CRuntimeClass ist neu in diesem Zusammenhang Archiv WriteObject dieser Klasse eine neue PID zugewiesen, und fügt es in das Archiv, vorangestellt der wNewClassTag Wert.
Der Deskriptor für diese Klasse wird dann eingefügt, in das Archiv mit den CRuntimeClass::Store Methode.CRuntimeClass::StoreFügt die Schemanummer der Klasse (siehe unten) und ASCII-Text-Name der Klasse.Beachten Sie, dass die Verwendung von ASCII-Text-Name die Eindeutigkeit des Archivs über Anwendungen hinweg nicht garantiert.Aus diesem Grund sollten Sie Ihre Datendateien zum Verhindern einer Beschädigung tag.Nach der Einfügung der Klasseninformationen legt das Archiv das Objekt in der m_pStoreMap und ruft dann die Serialize -Methode, um eine klassenspezifische Daten einfügen.Platzieren das Objekt in der m_pStoreMap vor dem Aufruf Serialize verhindert, dass mehrere Kopien des Objekts im Speicher gespeichert.
Bei der Rückgabe an den ursprünglichen Aufrufer (in der Regel der Stamm des Netzwerks von Objekten) müssen Sie CArchive::Close.Wenn Sie planen, andere durchzuführen CFileOperationen, müssen Sie die CArchive Methode Flush zum Verhindern einer Beschädigung des Archivs.
Hinweis |
---|
Diese Implementierung erzwingt eine feste Begrenzung von 0x3FFFFFFE Indizes pro Archivkontext.Diese Zahl stellt die maximale Anzahl von eindeutigen Objekte und Klassen, die in einem einzigen Archiv gespeichert werden können kann, aber eine einzelne Datenträgerdatei eine unbegrenzte Anzahl von Archiv-Kontexten. |
Laden die Objekte aus dem Speicher (CArchive::ReadObject)
Verwendet Objekte laden (Extrahieren) der CArchive::ReadObject Methode und ist die Umkehrung der WriteObject.Wie bei WriteObject, ReadObject heißt nicht direkt durch Benutzercode; Benutzercode sollte den typsichere Extraktionsoperator, der ruft rufen ReadObject mit den erwarteten CRuntimeClass.Dadurch wird die Integrität Typ des Vorgangs Extrakt sichergestellt.
Da die WriteObject Umsetzung zugewiesen zunehmende PIDs, beginnend mit 1 (0 ist vordefiniert, wie das NULL-Objekt), die ReadObject Implementierung kann ein Array verwenden, um den Status des Kontexts Archiv beizubehalten.Wenn eine PID gelesen aus dem Speicher, wenn die PID größer als die aktuelle obere Grenze ist die m_pLoadArray, ReadObject weiß, dass ein neues Objekt (oder Klassenbeschreibung) folgt.
Schema-Zahlen
Die Schema-Anzahl, die der Klasse zugeordnet ist als der IMPLEMENT_SERIAL -Methode der Klasse gefunden wird, ist die "Version" die Implementierung der Klasse.Das Schema bezieht sich auf die Implementierung der Klasse, nicht zu oft ein bestimmtes Objekt wurde persistent (in der Regel die Objektversion genannt).
Wenn Sie beabsichtigen, mehrere unterschiedliche Implementierungen der gleichen Klasse dauerhaft aufrechtzuerhalten, erhöhen Sie das Schema, wie Sie Ihr Objekts überarbeiten Serialize Implementierung der Methode ermöglicht es Ihnen, Code zu schreiben, die mit ältere Versionen der Durchführung gespeicherten Objekte laden können.
Die CArchive::ReadObject Methode löst eine CArchiveException Wenn es findet eine Schemanummer im beständigen Speicher, die die Schemanummer Klassenbeschreibung im Speicher unterscheidet.Es ist nicht einfach zu dieser Ausnahme beheben.
Sie können VERSIONABLE_SCHEMA kombiniert mit (bitweise OR) die Schemaversion halten diese Ausnahme ausgelöst.Mithilfe von VERSIONABLE_SCHEMA, Code kann die entsprechende Aktion seiner Serialize Funktion von der Rückgabewert von CArchive::GetObjectSchema.
Aufruf direkt serialisieren.
In vielen Fällen den Aufwand für das allgemeine Objekt Archiv-Schema der WriteObject und ReadObject ist nicht erforderlich.Dies ist der allgemeine Fall Serialisieren von Daten in ein CDocument.In diesem Fall die Serialize Methode der CDocument ist nicht mit den Operatoren extrahieren oder Insert direkt aufgerufen.Der Inhalt des Dokuments können wiederum das allgemeine Objekt Archiv-Schema verwenden.
Aufrufen von Serialize direkt hat die folgenden vor- und Nachteile:
Keine zusätzlichen Bytes werden hinzugefügt, um das Archiv vor oder nachdem das Objekt serialisiert wird.Dies nicht nur macht die gespeicherten Daten kleiner, aber können Sie implementieren Serialize Dateiformate für Routinen, die jede verarbeiten können.
MFC ist optimiert so die WriteObject und ReadObject -Implementierungen und zugehörige Auflistungen nicht verknüpft werden sollen in Ihre Anwendung, wenn Sie das allgemeine Objekt Archiv-Schema für andere Zwecke benötigen.
Der Code keinen zur Wiederherstellung der alten Schema Zahlen.Dadurch wird der Code des Dokument-Serialisierung verantwortlich für die Codierung Schema Zahlen, Format-Versionsnummern oder was auch immer identifizieren Sie Zahlen am Anfang Ihrer Datendateien verwenden.
Jedes Objekt, das mit einem direkten Aufruf serialisiert wird Serialize nicht verwenden, müssen CArchive::GetObjectSchema oder Handle einen Rückgabewert (UINT)-1 müssen, werden, dass die Version unbekannt war.
Da Serialize wird aufgerufen, direkt auf das Dokument kann nicht in der Regel für die Unterobjekte des Dokuments Verweise auf ihre übergeordnete Dokument archivieren.Erhalten diese Objekte müssen einen Zeiger sein Container-Dokument explizit oder müssen Sie CArchive::MapObject Funktion, mit der CDocument Zeiger auf eine PID, bevor diese Zeiger zurück archiviert werden.
Wie bereits erwähnt, sollten Sie codieren Sie die Version und die Klasse selbst beim Aufrufen von Serialize direkt, sodass Sie später das Format ändern, während gleichzeitig die Abwärtskompatibilität mit älteren Dateien.Die CArchive::SerializeClass Funktion kann explizit aufgerufen werden, bevor Sie direkt Serialisieren eines Objekts oder eine Basisklasse aufrufen.