TN002: Persistente Objekt-Datenformat

Diese Applikationsschrift beschreibt die MFC-Routinen, die persistente Objekte in C++ und das Format der Daten des Objekts unterstützen, wenn sie in einer Datei gespeichert werden. Dies gilt nur für Klassen mit den Makros DECLARE_SERIAL und IMPLEMENT_SERIAL.

Das Problem

Die MFC-Implementierung für persistente Daten verwendet ein kompaktes binäres Format für die Daten für viele Objekte in einem einzigen zusammenhängenden Teil einer Datei zu speichern. Das binäre Format ermöglicht die Struktur wie die Daten gespeichert werden, aber es ist das Objekt Serialize -Memberfunktion, die die eigentlichen Daten gespeichert, die vom Objekt bereitstellt.

Die MFC löst das strukturierende Problem mithilfe der Klasse CArchive. Ein CArchive -Objekt stellt ein Kontext für die Dauerhaftigkeit, die aus der Zeit das Archiv dauert erstellt wird, bis die CArchive::Close -Memberfunktion aufgerufen wird entweder explizit durch den Programmierer oder implizit durch den Destruktor, wenn der Bereich enthält die CArchive beendet wird,.

Diese Applikationsschrift beschreibt die Implementierung der Member CArchive ReadObject und WriteObject. ReadObject und WriteObject sind nicht direkt aufgerufen werden stattdessen von klassenspezifische typsichere einfügen und extrahieren Operatoren automatisch generiert durch die Makros DECLARE_SERIAL und IMPLEMENT_SERIAL verwendet.

klasse CMyObject: public CObject
{
 Nbsp;  DECLARE_SERIAL(CMyObject)
};

IMPLEMENT_SERIAL (einer, CObject, 1)

/ / Verwendungsbeispiel (Ar ist ein CArchive &)
CMyObject * pObj;
CArchive & Ar;
AR << pObj;        / / Ruft Ar.WriteObject(pObj)
AR >> pObj;        / / Ruft Ar.ReadObject(RUNTIME_CLASS(CObj))

Diese Applikationsschrift beschreibt Code befindet sich in der MFC-Quelldatei ARCOBJ.CPP. Die wichtigste CArchive -Implementierung finden Sie in ARCCORE.CPP.

Speichern von Objekten im Speicher (CArchive::WriteObject)

Die Memberfunktion CArchive::WriteObject schreibt Headerdaten verwendet, um das Objekt zu rekonstruieren. Diese Daten bestehen aus zwei Teilen: der Typ des Objekts und den Zustand des Objekts. Dieser Member-Funktion ist auch dafür verantwortlich, dass die Identität des Objekts geschrieben wird, so dass nur eine einzige Kopie gespeichert werden, unabhängig von der Anzahl der Zeiger auf das Objekt (einschließlich kreisförmigen Zeiger).

(Einfügen) speichern und Wiederherstellen von Objekten (extrahierende) stützt sich auf mehrere "Manifestkonstanten." Dies sind 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 Verwendet für NULL-Objektzeigern (0).
wNewClassTag Gibt an, dass die Klassenbeschreibung, die folgt ist neu in dieses Archiv-context—(-1).
wOldClassTag Gibt an, dass die Klasse des Objekts gelesen werden in diesem Zusammenhang (0 x 8000) erlebt hat.

Wenn Objekte zu speichern, verwaltet das Archiv eine CMapPtrToPtr (die M_pStoreMap) ist eine Zuordnung zwischen einer gespeicherten Objekts eine 32-Bit persistenten Bezeichner (PID). Eine PID ist zugewiesen jedes eindeutige Objekt und jeder eindeutigen Klassennamen, die im Rahmen des Archivs gespeichert ist. Diese PIDs werden sequenziell ab 1 ausgehändigt. Es ist wichtig zu beachten, dass diese PIDs keine Bedeutung außerhalb des Bereichs des Archivs haben und, insbesondere, sind nicht zu verwechseln mit Rekordzahl oder andere Identität.

Ab MFC, Version 4.0 die CArchive -Klasse wurde erweitert, um sehr große Archiven zu unterstützen. In früheren Versionen wurde eine PID eine 16-Bit-Menge, das Archiv zu 0x7FFE (32766) Objekte begrenzen. Primäre Immundefekte sind jetzt 32-Bit, aber sie sind aus als 16-Bit geschrieben, es sei denn sie größer als 0x7FFE sind. Große PIDs werden als 0x7FFF, gefolgt von der 32-Bit großen PID geschrieben. Diese Technik behält die Abwärtskompatibilität Datei.

Wenn ein Antrag gestellt wird, um ein Objekt in ein Archiv (in der Regel durch den globalen Einfügemarke Operator) speichern, wird für einen Zeiger NULL CObject geprüft; Wenn der Zeiger NULL ist, wird die wNullTag in den Archiv-Stream eingefügt.

Haben wir einen echtes Objektzeiger, die serialisiert werden kann (die Klasse ist eine Klasse DECLARE_SERIAL ), wir prüfen dann die M_pStoreMap zu sehen, ob das Objekt bereits gespeichert wurde. Wenn ja, fügen wir die 32-Bit-PID mit diesem Objekt verbunden.

Wenn das Objekt nicht zuvor gespeichert wurde, gibt es zwei Möglichkeiten, die wir berücksichtigen: entweder das Objekt und den genauen Typ des Objekts (d. h. Klasse) sind neu in diesem Archiv, oder das Objekt hat einen genauen Typ schon gesehen. Um festzustellen, ob der Typ erlebt hat, dass wir die M_pStoreMap für ein CRuntimeClass -Objekt Abfragen, die entspricht das CRuntimeClass -Objekt zugeordnete das Objekt, die, das wir speichern. Wenn wir diese Klasse vor gesehen haben, fügt WriteObject ein Tag, der die bitweise OR'ing wOldClassTag und dieses Index ist. Wenn die CRuntimeClass zu diesem Archiv neu ist, dann WriteObject weist eine neue PID zu dieser Klasse und fügen Sie ihn in das Archiv, vorangegangen durch den wNewClassTag -Wert.

Der Deskriptor für diese Klasse wird dann in das Archiv mit Hilfe der Memberfunktion CRuntimeClass Storeeingefügt. CRuntimeClass::Store fügt die Schema-Anzahl der Klasse (siehe unten) und der ASCII-Text-Name der Klasse. Beachten Sie, dass die Verwendung von ASCII-Text-Name garantiert keine Eindeutigkeit des Archivs über Anwendungen hinweg, so ist es ratsam, Ihre Datendateien zum Verhindern einer Beschädigung zu kennzeichnen. Nach der Einfügung der Klasseninformationen das Archiv platziert das Objekt in der M_pStoreMap und ruft dann die Memberfunktion Serialize klassenspezifische Daten in das Archiv eingefügt. Platzieren das Objekt in der M_pStoreMap vor dem Aufruf der Serialize-Methode verhindert, dass mehrere Kopien des Objekts im Speicher gespeichert.

Bei der Rückkehr an den ursprünglichen Aufrufer (in der Regel der Stamm des Netzes der Objekte), ist es wichtig, Schließen das Archiv. Wenn andere CFile -Operationen durchgeführt werden, muss die CArchive -Memberfunktion Flush aufgerufen werden. Andernfalls führt ein korrupt Archiv.

Hinweis&Nbsp;  Diese Implementierung legt eine feste Begrenzung von 0x3FFFFFFE Indizes pro Archiv Kontext. Diese Zahl stellt die maximale Anzahl von einzigartigen Objekten und Klassen, die in einem einzigen Archiv gespeichert werden können kann, aber beachten Sie, dass eine einzelne Datei Datenträger eine unbegrenzte Anzahl von Kontexten Archiv.

Laden Objekte aus dem Store (CArchive::ReadObject)

Laden (extrahieren) Objekte wird die CArchive::ReadObject -Memberfunktion verwendet und ist das Gegenteil von WriteObject. Wie mit WriteObject, wird ReadObject nicht direkt durch Benutzercode genannt; Benutzercode sollte den typsichere Extraktion-Operator aufrufen, der wodurch die ReadObject mit der erwarteten CRuntimeClassaufruft. Dies sichert die Integrität der Typ des Vorgangs Extrakt.

Da die Implementierung der WriteObject zunehmende PIDs zugewiesen, beginnend mit 1 (0 ist vordefiniert, wie das NULL-Objekt), die ReadObject -Implementierung kann ein Array verwenden, um den Status des Kontexts ein Archiv verwalten. Wenn eine PID aus dem Speicher gelesen wird, wenn die PID größer als die aktuelle obere Grenze der M_pLoadArray ist, weiß dann ReadObject , dass ein neues Objekt (Klassenbeschreibung oder).

Schema Zahlen

Die Schema-Anzahl, die der Klasse zugeordnet wird, wenn die Klasse IMPLEMENT_SERIAL aufgetreten ist, ist die "Version" der Implementierung der Klasse. Das Schema bezieht sich auf die Implementierung der Klasse, nicht auf die Anzahl der Male ein bestimmtes Objekt erzielt worden persistent (in der Regel die Objektversion genannt).

We&nn Sie beabsichtigen, im Laufe der Zeit mehrere unterschiedliche Implementierungen der gleichen Klasse pflegen, ermöglicht erhöht das Schema, wie Sie Ihr Objekt Serialize -Funktionsimplementierung überarbeiten es Ihnen, Code zu schreiben, die mit älteren Versionen von den implementation.nbsp gespeicherten Objekte laden können;

CArchive::ReadObject Member-Funktion löst eine CArchiveException wenn es eine Schemanummer im beständigen Speicher trifft, die die Schema-Anzahl der Beschreibung der Klasse im Arbeitsspeicher unterscheidet. Es ist nicht leicht, diese Ausnahme beheben.

Sie können VERSIONABLE_SCHEMA verwenden oder hatte mit Ihrer Schemaversion zu halten diese Ausnahme wird ausgelöst. Mit VERSIONABLE_SCHEMA, kann Ihr Code die entsprechende Aktion in ihrer Funktion Serialize nehmen durch Überprüfung des Rückgabewertes von CArchive::GetObjectSchema.

Aufruf serialisieren direkt

Es gibt viele Fälle, in denen der Aufwand für das allgemeine Objekt Archiv Schema der WriteObject und ReadObject nicht erforderlich oder gewünscht ist. Dies ist der allgemeine Fall der Serialisierung der Daten in ein CDocument. In diesem Fall die Serialize -Memberfunktion der CDocument heißt direkt, nicht mit dem Auszug oder Operatoren einfügen. Der Inhalt des Dokuments können wiederum das allgemeine Objekt Archiv-Schema verwenden.

Direkter Aufruf der Serialize-Methode hat die folgenden vor- und Nachteile:

Da Serialize direkt in Ihr Dokument aufgerufen wird, ist es normalerweise nicht möglich für die Unterobjekte des Dokuments verweisen auf ihre übergeordnete Dokument archivieren. Diese Objekte müssen gegeben werden einen Zeiger sein Container-Dokument explizit oder CArchive::MapObject Funktion verwenden Sie, um eine PID den CDocument -Zeiger zuzuordnen, bevor diese zurück Zeiger archiviert werden.

Wie bereits erwähnt, sollten Sie die Informationen zu Version und Klasse selbst codieren beim Aufrufen der Serialize direkt, so dass Sie zum Ändern des Formats später gleichzeitig Abwärtskompatibilität mit älteren Dateien. Die CArchive::SerializeClassRef -Funktion kann explizit aufgerufen werden, bevor direkt Serialisierung eines Objekts oder eine Basisklasse aufrufen.

Technische Hinweise von &Nummer |nbsp; Technische Hinweise nach Kategorie

Index