Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Hinweis werden die MFC-Routinen beschrieben, die persistente C++-Objekte und das Format der Objektdaten 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 Datenspeicherung speichert Daten vieler Objekte in einem einzigen zusammenhängenden Teil einer Datei. Die Methode des Serialize Objekts übersetzt die Daten des Objekts in ein kompaktes Binärformat.
Die Implementierung garantiert, dass alle Daten mithilfe der CArchive-Klasse im selben Format gespeichert werden. Es verwendet ein CArchive Objekt als Übersetzer. Dieses Objekt wird ab dem Zeitpunkt der Erstellung beibehalten, bis Sie CArchive::Close aufrufen. Diese Methode kann entweder explizit vom Programmierer oder implizit vom Destruktor aufgerufen werden, wenn das Programm den Bereich beendet, der die CArchiveDatei enthält.
In dieser Notiz wird die Implementierung der CArchive Mitglieder CArchive::ReadObject und CArchive::WriteObject beschrieben. Sie finden den Code für diese Funktionen in Arcobj.cpp und die Hauptimplementierung für CArchive in Arccore.cpp. Der Benutzercode ruft ReadObject und WriteObject nicht direkt auf. Stattdessen werden diese Objekte von klassenspezifischen typsicheren Einfüge- und Extraktionsoperatoren verwendet, die automatisch von den DECLARE_SERIAL und IMPLEMENT_SERIAL Makros generiert werden. Der folgende Code zeigt, wie WriteObject und ReadObject implizit aufgerufen wird:
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 Kopfzeilendaten, die zum Rekonstruieren des Objekts verwendet werden. Diese Daten bestehen aus zwei Teilen: dem Typ des Objekts und dem Status des Objekts. Diese Methode ist auch für die Aufrechterhaltung der Identität des objekts verantwortlich, das ausgeschrieben wird, sodass nur eine einzelne Kopie gespeichert wird, unabhängig von der Anzahl der Zeiger auf dieses Objekt (einschließlich Zirkelzeiger).
Das Speichern (Einfügen) und Wiederherstellen (Extrahieren) von Objekten basiert auf mehreren "Manifestkonstanten". Dies sind Werte, die in binär gespeichert sind und wichtige Informationen für das Archiv bereitstellen (beachten Sie, dass das Präfix "w" 16-Bit-Mengen angibt):
| Etikett | BESCHREIBUNG |
|---|---|
| wNullTag | Wird für NULL-Objektzeiger (0) verwendet. |
| wNewClassTag | Gibt die folgende Klassenbeschreibung an, die in diesem Archivkontext neu ist (-1). |
| wOldClassTag | Gibt an, dass die Klasse des gelesenen Objekts in diesem Kontext (0x8000) angezeigt wurde. |
Beim Speichern von Objekten verwaltet das Archiv einen CMapPtrToPtr ( die m_pStoreMap), bei dem es sich um eine Zuordnung von einem gespeicherten Objekt zu einem 32-Bit-persistenten Bezeichner (PID) handelt. Jedem eindeutigen Objekt und jedem eindeutigen Klassennamen, der im Kontext des Archivs gespeichert wird, wird eine PID zugewiesen. Diese PIDs werden sequenziell ab 1 weitergegeben. Diese PIDs haben keine Bedeutung außerhalb des Archivbereichs und sind insbesondere nicht mit Datensatznummern oder anderen Identitätselementen zu verwechseln.
In der CArchive Klasse sind PIDs 32-Bit, aber sie werden als 16-Bit-Version geschrieben, es sei denn, sie sind größer als 0x7FFE. Große PIDs werden als 0x7FFF gefolgt von der 32-Bit-PID geschrieben. Dadurch wird die Kompatibilität mit Projekten beibehalten, die in früheren Versionen erstellt wurden.
Wenn eine Anforderung zum Speichern eines Objekts in einem Archiv (in der Regel mithilfe des globalen Einfügeoperators) erfolgt, wird eine Überprüfung für einen NULL-CObject-Zeiger durchgeführt. Wenn der Zeiger NULL ist, wird das wNullTag in den Archivdatenstrom eingefügt.
Wenn der Zeiger nicht NULL ist und serialisiert werden kann (die Klasse ist eine DECLARE_SERIAL Klasse), überprüft der Code die m_pStoreMap , um festzustellen, ob das Objekt bereits gespeichert wurde. Falls ja, fügt der Code die diesem Objekt zugeordnete 32-Bit-PID in den Archivdatenstrom ein.
Wenn das Objekt noch nicht gespeichert wurde, sind zwei Möglichkeiten zu berücksichtigen: entweder sind das Objekt und der genaue Typ (d. h. die Klasse) des Objekts in diesem Archivkontext neu, oder das Objekt ist bereits ein in diesem Archivkontext bekannter exakter Typ. Um festzustellen, ob der Typ angezeigt wurde, fragt der Code die m_pStoreMap für ein CRuntimeClass-Objekt ab, das dem CRuntimeClass objekt zugeordneten Objekt entspricht, das gespeichert wird. Wenn eine Übereinstimmung vorhanden ist, fügt WriteObject einen Tag ein, der das bitweise OR von wOldClassTag und diesem Index darstellt. Wenn das CRuntimeClass neu in diesem Archivkontext ist, weist WriteObject dieser Klasse eine neue PID zu und fügt sie in das Archiv ein, wobei der Wert wNewClassTag vorangestellt wird.
Der Deskriptor für diese Klasse wird dann mithilfe der CRuntimeClass::Store Methode in das Archiv eingefügt.
CRuntimeClass::Store fügt die Schemanummer der Klasse (siehe unten) und den ASCII-Textnamen der Klasse ein. Beachten Sie, dass die Verwendung des ASCII-Textnamens nicht die Eindeutigkeit des Archivs in allen Anwendungen garantiert. Daher sollten Sie Ihre Datendateien markieren, um Beschädigungen zu verhindern. Nach dem Einfügen der Klasseninformationen platziert das Archiv das Objekt in die m_pStoreMap und ruft dann die Serialize Methode auf, um klassenspezifische Daten einzufügen. Wenn Sie das Objekt in die m_pStoreMap setzen, bevor Sie aufrufen Serialize , wird verhindert, dass mehrere Kopien des Objekts im Speicher gespeichert werden.
Wenn Sie zum anfänglichen Aufrufer zurückkehren (in der Regel das Stammverzeichnis des Netzwerks von Objekten), müssen Sie CArchive::Close aufrufen. Wenn Sie beabsichtigen, andere CFile-Vorgänge auszuführen, müssen Sie die CArchive Methode Flush aufrufen, um Beschädigungen des Archivs zu verhindern.
Hinweis
Diese Implementierung legt eine harte Grenze von 0x3FFFFFFE Indizes pro Archivkontext fest. Diese Zahl stellt die maximale Anzahl eindeutiger Objekte und Klassen dar, die in einem einzelnen Archiv gespeichert werden können, aber eine einzelne Datenträgerdatei kann eine unbegrenzte Anzahl von Archivkontexten aufweisen.
Laden von Objekten aus dem Store (CArchive::ReadObject)
Das Laden (Extrahieren) von Objekten erfolgt mithilfe der CArchive::ReadObject Methode und ist das Gegenstück zu WriteObject. Wie bei WriteObject wird ReadObject nicht direkt vom Benutzercode aufgerufen. Der Benutzercode sollte den typsicheren Extraktionsoperator aufrufen, der ReadObject mit dem erwarteten CRuntimeClass aufruft. Dadurch wird die Typintegrität des Extraktvorgangs sichergestellt.
Da die WriteObject Implementierung zunehmenden PIDs zugewiesen wurde, beginnend mit 1 (0 ist als NULL-Objekt vordefiniert), kann die ReadObject Implementierung ein Array verwenden, um den Status des Archivkontexts beizubehalten. Wenn eine PID aus dem Speicher gelesen wird und die PID größer als die aktuelle Obergrenze der m_pLoadArray ist, weiß ReadObject, dass ein neues Objekt (oder eine Klassenbeschreibung) folgt.
Schemanummern
Die Schemanummer, die der Klasse zugewiesen ist, wenn die IMPLEMENT_SERIAL Methode der Klasse gefunden wird, ist die "Version" der Klassenimplementierung. Das Schema bezieht sich auf die Implementierung der Klasse, nicht auf die Häufigkeit, mit der ein bestimmtes Objekt dauerhaft gemacht wurde (in der Regel als Objektversion bezeichnet).
Wenn Sie über einen längeren Zeitraum hinweg verschiedene Implementierungen derselben Klasse beibehalten möchten, sollten Sie das Schema erhöhen, wenn Sie die Implementierung der Methode Serialize Ihres Objekts überarbeiten. Auf diese Weise können Sie Code schreiben, der Objekte laden kann, die mithilfe älterer Versionen der Implementierung gespeichert wurden.
Die CArchive::ReadObject Methode löst eine CArchiveException aus, wenn eine Schemanummer im persistenten Speicher auftritt, die sich von der Schemanummer der Klassenbeschreibung im Arbeitsspeicher unterscheidet. Es ist nicht einfach, sich von dieser Ausnahme zu erholen.
Sie können VERSIONABLE_SCHEMA in Kombination mit Ihrer Schemaversion (bitweise ODER) verwenden, damit diese Ausnahme nicht ausgelöst wird. Mithilfe von VERSIONABLE_SCHEMA kann ihr Code die entsprechende Aktion in seiner Serialize Funktion ausführen, indem der Rückgabewert von CArchive::GetObjectSchema überprüft wird.
Direktes Serialisieren von Anrufen
In vielen Fällen ist der Aufwand des allgemeinen Objektarchivschemas WriteObject und ReadObject nicht erforderlich. Dies ist der häufige Fall der Serialisierung der Daten in ein CDocument. In diesem Fall wird die Serialize-Methode der CDocument-Klasse direkt aufgerufen, nicht mit den Extraktions- oder Einfügeoperatoren. Der Inhalt des Dokuments kann wiederum das allgemeinere Objektarchivschema verwenden.
Das Direkte Anrufen Serialize hat die folgenden Vor- und Nachteile:
Dem Archiv werden vor oder nach der Serialisierung des Objekts keine zusätzlichen Bytes hinzugefügt. Dadurch werden die gespeicherten Daten nicht nur kleiner, sondern Sie können Routinen implementieren
Serialize, die alle Dateiformate verarbeiten können.Der MFC ist so abgestimmt, dass die
WriteObjectImplementierungen undReadObjectverwandten Auflistungen nicht mit Ihrer Anwendung verknüpft werden, es sei denn, Sie benötigen das allgemeinere Objektarchivschema für einen anderen Zweck.Ihr Code muss nicht aus alten Schemanummern wiederhergestellt werden. Dadurch ist Ihr Dokument-Serialisierungscode verantwortlich für die Codierung der Schemanummern, der Dateiformat-Versionsnummern oder anderer Identifikationsnummern, die Sie am Anfang Ihrer Datendateien verwenden.
Jedes Objekt, das mit einem direkten Aufruf von
Serializeserialisiert wird, darfCArchive::GetObjectSchemanicht verwenden oder muss einen Rückgabewert von (UINT)-1, der angibt, dass die Version unbekannt war, verarbeiten.
Da Serialize direkt in Ihrem Dokument aufgerufen wird, ist es in der Regel für die Unterobjekte des Dokuments nicht möglich, Verweise auf das übergeordnete Dokument zu archivieren. Diese Objekte müssen explizit einem Zeiger auf das Containerdokument zugewiesen werden, oder Sie müssen die CArchive::MapObject-Funktion verwenden, um den CDocument Zeiger einer PID zuzuordnen, bevor diese Zurückzeiger archiviert werden.
Wie bereits erwähnt, sollten Sie die Versions- und Klasseninformationen selbst codieren, wenn Sie direkt aufrufen Serialize , sodass Sie das Format später ändern und gleichzeitig die Abwärtskompatibilität mit älteren Dateien beibehalten. Die CArchive::SerializeClass Funktion kann explizit aufgerufen werden, bevor ein Objekt direkt serialisiert wird oder bevor eine Basisklasse aufgerufen wird.
Siehe auch
Technische Hinweise nach Nummer
Technische Hinweise nach Kategorie