Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här anteckningen beskriver de MFC-rutiner som stöder beständiga C++-objekt och formatet på objektdata när de lagras i en fil. Detta gäller endast klasser med makron DECLARE_SERIAL och IMPLEMENT_SERIAL .
Problemet
MFC-implementeringen för beständiga data lagrar data för många objekt i en enda sammanhängande del av en fil. Objektets Serialize metod översätter objektets data till ett kompakt binärt format.
Implementeringen garanterar att alla data sparas i samma format med hjälp av CArchive-klassen. Det använder ett CArchive objekt som översättare. Det här objektet bevaras från det att det skapas tills du anropar CArchive::Close. Den här metoden kan anropas antingen explicit av programmeraren eller implicit av destruktor när programmet avslutar det omfång som innehåller objektet CArchive.
Den här anteckningen beskriver implementeringen av CArchive medlemmarna CArchive::ReadObject och CArchive::WriteObject. Du hittar koden för dessa funktioner i Arcobj.cpp och huvudimplementeringen för CArchive i Arccore.cpp. Användarkoden anropar inte direkt ReadObject och WriteObject. I stället används dessa objekt av klassspecifika typsäkra infognings- och extraheringsoperatorer som genereras automatiskt av DECLARE_SERIAL- och IMPLEMENT_SERIAL makron. Följande kod visar hur WriteObject och ReadObject kallas implicit:
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))
Spara objekt till lagringen (CArchive::WriteObject)
Metoden CArchive::WriteObject skriver huvuddata som används för att rekonstruera objektet. Dessa data består av två delar: objektets typ och objektets tillstånd. Den här metoden ansvarar också för att upprätthålla identiteten för objektet som skrivs ut, så att endast en enda kopia sparas, oavsett antalet pekare till objektet (inklusive cirkelpekare).
Om du sparar (infogar) och återställer (extraherar) objekt används flera "manifestkonstanter". Det här är värden som lagras i binärt värde och ger viktig information till arkivet (observera att prefixet "w" anger 16-bitars kvantiteter):
| Tagg | Beskrivning |
|---|---|
| wNullTag | Används för NULL-objektpekare (0). |
| wNewClassTag | Anger att klassbeskrivningen som följer är ny i detta arkivsammanhang (-1). |
| wOldClassTag | Anger att klassen för objektet som läses har setts i detta sammanhang (0x8000). |
När objekt lagras har arkivet en CMapPtrToPtr ( m_pStoreMap) som är en mappning från ett lagrat objekt till en 32-bitars beständig identifierare (PID). En PID tilldelas till varje unikt objekt och varje unikt klassnamn som sparas i arkivets kontext. Dessa PID delas ut sekventiellt från och med 1. Dessa PID:er har ingen betydelse utanför arkivets omfång och ska i synnerhet inte förväxlas med postnummer eller andra identitetsobjekt.
CArchive I klassen är PID:er 32-bitars, men de skrivs ut som 16-bitars om de inte är större än 0x7FFE. Stora PID:er skrivs som 0x7FFF följt av 32-bitars PID. Detta upprätthåller kompatibilitet med projekt som skapades i tidigare versioner.
När en begäran görs om att spara ett objekt i ett arkiv (vanligtvis med hjälp av den globala insättningsoperatorn) görs en kontroll av en NULL CObject-pekare . Om pekaren är NULL infogas wNullTag i arkivströmmen.
Om pekaren inte är NULL och kan serialiseras (klassen är en DECLARE_SERIAL klass) kontrollerar koden m_pStoreMap för att se om objektet redan har sparats. Om den har det infogar koden den 32-bitars PID som är associerad med objektet i arkivströmmen.
Om objektet inte har sparats tidigare finns det två möjligheter att tänka på: antingen är både objektet och den exakta typen (dvs. klass) för objektet nytt för den här arkivkontexten, eller så är objektet av en exakt typ som redan har setts. För att avgöra om typen har setts frågar koden m_pStoreMap efter ett CRuntimeClass-objekt som matchar objektet CRuntimeClass som är associerat med objektet som sparas. Om det finns en matchning, WriteObject infogas en tagg som är den bitvisa OR av wOldClassTag och det här indexet. Om CRuntimeClass är nytt för den här arkivkontexten, tilldelar WriteObject en ny PID till den klassen och infogar den i arkivet, före värdet wNewClassTag.
Beskrivningen för den här klassen infogas sedan i arkivet med hjälp av CRuntimeClass::Store metoden .
CRuntimeClass::Store infogar schemanumret för klassen (se nedan) och ASCII-textnamnet för klassen. Observera att användningen av ASCII-textnamnet inte garanterar att arkivet är unikt för alla program. Därför bör du tagga dina datafiler för att förhindra skador. Efter infogningen av klassinformationen placerar arkivet objektet i m_pStoreMap och anropar Serialize sedan metoden för att infoga klassspecifika data. Om du placerar objektet i m_pStoreMap innan Serialize anropas, förhindras flera kopior av objektet från att sparas i lagringen.
När du återvänder till den första anroparen (vanligtvis roten i nätverket av objekt) måste du anropa CArchive::Close. Om du planerar att utföra andra CFile-åtgärder måste du anropa CArchive metoden Flush för att förhindra att arkivet skadas.
Anmärkning
Den här implementeringen medför en hård gräns på 0x3FFFFFFE index per arkivkontext. Det här talet representerar det maximala antalet unika objekt och klasser som kan sparas i ett enda arkiv, men en enskild diskfil kan ha ett obegränsat antal arkivkontexter.
Läser in objekt från butiken (CArchive::ReadObject)
Att läsa in (extrahera) objekt använder metod CArchive::ReadObject och det är motsatsen till WriteObject. Precis som med WriteObjectanropas ReadObject inte direkt av användarkod. Användarkod bör anropa den typsäkra extraheringsoperatorn som anropar ReadObject med den förväntade CRuntimeClass. Detta försäkrar typintegriteten för extraheringsåtgärden.
WriteObject Eftersom implementeringen tilldelade ökande PID:er, från och med 1 (0 är fördefinierad som NULL-objektet), ReadObject kan implementeringen använda en matris för att upprätthålla arkivkontextens tillstånd. När en PID läses från arkivet, om PID är större än den aktuella övre gränsen för m_pLoadArray, vet ReadObject att ett nytt objekt (eller klassbeskrivning) följer.
Schemanummer
Schemanumret, som tilldelas till klassen när IMPLEMENT_SERIAL metoden för klassen påträffas, är "versionen" av klassimplementeringen. Schemat refererar till implementeringen av klassen, inte antalet gånger ett angivet objekt har gjorts beständigt (kallas vanligtvis objektversion).
Om du tänker underhålla flera olika implementeringar av samma klass över tid kan du skriva kod som kan läsa in objekt som lagras med hjälp av äldre versioner av implementeringen genom att öka schemat när du ändrar implementeringen av objektets Serialize metod.
Metoden CArchive::ReadObject genererar en CArchiveException när den stöter på ett schemanummer i det beständiga arkivet som skiljer sig från schemanumret för klassbeskrivningen i minnet. Det är inte lätt att hantera detta undantag.
Du kan använda VERSIONABLE_SCHEMA kombinerat med (bitvis ELLER) din schemaversion för att förhindra att det här undantaget genereras. Genom att använda VERSIONABLE_SCHEMA kan din kod vidta lämpliga åtgärder i sin Serialize-funktion genom att kontrollera returvärdet från CArchive::GetObjectSchema.
Anropa Serialisera direkt
I många fall är omkostnaderna för det allmänna objektarkivschemat för WriteObject och ReadObject inte nödvändigt. Detta är det vanliga fallet med serialisering av data till en CDocument. I det här fallet anropas Serialize:s CDocument-metod direkt, och inte med operatorerna extract eller insert. Innehållet i dokumentet kan i sin tur använda det mer allmänna objektarkivschemat.
Att ringa Serialize direkt har följande fördelar och nackdelar:
Inga extra byte läggs till i arkivet innan eller efter att objektet har serialiserats. Detta gör inte bara sparade data mindre, utan gör att du kan implementera
Serializerutiner som kan hantera alla filformat.MFC är justerat så att
WriteObjectochReadObject-implementeringarna och relaterade samlingar inte länkas till ditt program om du inte behöver det mer allmänna objektarkivschemat för något annat ändamål.Koden behöver inte återställas från gamla schemanummer. Detta gör att dokumentets serialiseringskod ansvarar för kodning av schemanummer, versionsnummer i filformat eller de identifieringsnummer som du använder i början av dina datafiler.
Alla objekt som serialiseras med ett direktanrop till
Serializefår inte användaCArchive::GetObjectSchemaeller måste hantera ett returvärde på (UINT)-1 som anger att versionen var okänd.
Eftersom Serialize anropas direkt i dokumentet är det vanligtvis inte möjligt för underobjekten i dokumentet att arkivera referenser till det överordnade dokumentet. Dessa objekt måste uttryckligen ges en pekare till containerdokumentet eller så måste du använda funktionen CArchive::MapObject för att mappa pekaren CDocument till en PID innan de här bakåtpekarna arkiveras.
Som tidigare nämnts bör du koda versionen och klassinformationen själv när du anropar Serialize direkt, så att du kan ändra formatet senare samtidigt som du bibehåller bakåtkompatibilitet med äldre filer. Funktionen CArchive::SerializeClass kan anropas explicit innan du serialiserar ett objekt direkt eller innan du anropar en basklass.
Se även
tekniska anteckningar efter nummer
tekniska anteckningar efter kategori