TN002: Kalıcı Nesne Veri Biçimi
Bu not, kalıcı C++ nesnelerini destekleyen MFC yordamlarını ve dosyada depolandığında nesne verilerinin biçimini açıklar. Bu yalnızca DECLARE_SERIAL ve IMPLEMENT_SERIAL makroları olan sınıflar için geçerlidir.
Sorun
Kalıcı veri için MFC uygulaması, bir dosyanın tek bir bitişik bölümünde birçok nesnenin verilerini depolar. nesnenin Serialize
yöntemi, nesnenin verilerini sıkıştırılmış ikili biçime çevirir.
Uygulama, CArchive Sınıfı kullanılarak tüm verilerin aynı biçimde kaydedilmesini garanti eder. Bir nesneyi çevirici olarak kullanır CArchive
. Bu nesne, oluşturulduğu zamandan siz CArchive::Close çağrısına kadar devam eder. Bu yöntem programcı tarafından açıkça veya program öğesini içeren CArchive
kapsamdan çıktığında yıkıcı tarafından örtük olarak çağrılabilir.
Bu not, CArchive::ReadObject ve CArchive::WriteObject üyelerinin uygulamasını CArchive
açıklar. Bu işlevlerin kodunu Arcobj.cpp dosyasında, ana uygulamasını CArchive
ise Arccore.cpp dosyasında bulabilirsiniz. Kullanıcı kodu ve öğesini doğrudan çağırmaz ReadObject
WriteObject
. Bunun yerine, bu nesneler DECLARE_SERIAL ve IMPLEMENT_SERIAL makroları tarafından otomatik olarak oluşturulan sınıfa özgü tür güvenli ekleme ve ayıklama işleçleri tarafından kullanılır. Aşağıdaki kod, ve'nin ReadObject
nasıl WriteObject
örtük olarak çağrıldığını gösterir:
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))
Nesneleri Depoya Kaydetme (CArchive::WriteObject)
yöntemi CArchive::WriteObject
, nesneyi yeniden yapılandırmak için kullanılan üst bilgi verilerini yazar. Bu veriler iki bölümden oluşur: nesnenin türü ve nesnenin durumu. Bu yöntem ayrıca, yazılan nesnenin kimliğini korumaktan da sorumludur; böylece bu nesnenin işaretçi sayısına bakılmaksızın (döngüsel işaretçiler dahil) yalnızca tek bir kopya kaydedilir.
Nesneleri kaydetme (ekleme) ve geri yükleme (ayıklama) birkaç "bildirim sabiti" gerektirir. Bunlar ikili olarak depolanan ve arşive önemli bilgiler sağlayan değerlerdir ("w" ön ekinin 16 bit miktarları gösterdiğine dikkat edin):
Etiket | Tanım |
---|---|
wNullTag | NULL nesne işaretçileri (0) için kullanılır. |
wNewClassTag | Aşağıdaki sınıf açıklamasının bu arşiv bağlamı (-1) için yeni olduğunu gösterir. |
wOldClassTag | Okunan nesnenin sınıfının bu bağlamda görüldüğünü gösterir (0x8000). |
Nesneleri depolarken arşiv, depolanan bir nesneden 32 bit kalıcı tanımlayıcıya (PID) eşleme olan bir CMapPtrToPtr ( m_pStoreMap) tutar. Her benzersiz nesneye ve arşiv bağlamında kaydedilen her benzersiz sınıf adına bir PID atanır. Bu PID'ler 1'den başlayarak sıralı olarak dağıtılır. Bu PID'lerin arşiv kapsamı dışında bir önemi yoktur ve özellikle de kayıt numaraları veya diğer kimlik öğeleriyle karıştırılmamalıdır.
CArchive
sınıfında, PID'ler 32 bittir, ancak 0x7FFE büyük olmadığı sürece 16 bit olarak yazılır. Büyük PID'ler 0x7FFF ve ardından 32 bit PID olarak yazılır. Bu, önceki sürümlerde oluşturulan projelerle uyumluluğu korur.
Bir nesneyi arşive kaydetme isteğinde bulunulduğunda (genellikle genel ekleme işleci kullanılarak), NULL CObject işaretçisi için bir denetim yapılır. İşaretçi NULL ise, wNullTag arşiv akışına eklenir.
İşaretçi NULL değilse ve seri hale getirilebilirse (sınıf bir DECLARE_SERIAL
sınıftır), kod nesnenin zaten kaydedilip kaydedilmediğini görmek için m_pStoreMap denetler. Varsa, kod bu nesneyle ilişkili 32 bit PID'yi arşiv akışına ekler.
Nesne daha önce kaydedilmediyse, dikkate alınması gereken iki olasılık vardır: nesnenin hem nesnesi hem de tam türü (yani, sınıf) bu arşiv bağlamında yenidir veya nesne zaten tam olarak görülen türdedir. Türün görülmüş olup olmadığını belirlemek için kod, kaydedilen nesneyle ilişkili nesneyle eşleşen bir CRuntimeClass nesnesinin CRuntimeClass
m_pStoreMap sorgular. Eşleşme varsa, WriteObject
wOldClassTag ve bu dizinin bit düzeyinde OR
bir etiket ekler. CRuntimeClass
bu arşiv bağlamında yeniyse, WriteObject
bu sınıfa yeni bir PID atar ve wNewClassTag değerinin önünde arşive ekler.
Bu sınıfın tanımlayıcısı daha sonra yöntemi kullanılarak arşive CRuntimeClass::Store
eklenir. CRuntimeClass::Store
sınıfının şema numarasını (aşağıya bakın) ve sınıfın ASCII metin adını ekler. ASCII metin adının kullanılmasının, uygulamalar arasında arşivin benzersizliğini garanti etmediğini unutmayın. Bu nedenle, bozulmayı önlemek için veri dosyalarınızı etiketlemeniz gerekir. Sınıf bilgilerinin eklenmesinden sonra arşiv, nesnesini m_pStoreMap yerleştirir ve ardından sınıfa özgü verileri eklemek için yöntemini çağırır Serialize
. Çağırmadan Serialize
önce nesnenin m_pStoreMap yerleştirilmesi, nesnenin birden çok kopyasının depoya kaydedilmesini önler.
İlk çağırana geri dönerken (genellikle nesne ağının kökü), CArchive::Close öğesini çağırmanız gerekir. Diğer CFileişlemlerini gerçekleştirmeyi planlıyorsanız, arşiv bozulmasını CArchive
önlemek için Flush yöntemini çağırmanız gerekir.
Dekont
Bu uygulama, arşiv bağlamı başına sabit 0x3FFFFFFE dizin sınırı uygular. Bu sayı, tek bir arşive kaydedilebilecek en fazla benzersiz nesne ve sınıf sayısını temsil eder, ancak tek bir disk dosyasının sınırsız sayıda arşiv bağlamı olabilir.
Depodan Nesne Yükleme (CArchive::ReadObject)
Nesneleri yükleme (ayıklama) yöntemini kullanır CArchive::ReadObject
ve öğesinin WriteObject
tersidir. gibiWriteObject
, ReadObject
kullanıcı kodu tarafından doğrudan çağrılmaz; kullanıcı kodu beklenen CRuntimeClass
ile çağıran tür güvenli ayıklama işlecini çağırmalıdırReadObject
. Bu, ayıklama işleminin tür bütünlüğünü sigortalar.
WriteObject
Uygulama, 1 ile başlayarak artan PID'ler atadığından (0 NULL nesnesi olarak önceden tanımlanmıştır), ReadObject
arşiv bağlamının durumunu korumak için bir dizi kullanabilir. Bir PID mağazadan okunduğunda, PID m_pLoadArray geçerli üst sınırından büyükse, ReadObject
yeni bir nesnenin (veya sınıf açıklamasının) takip ettiğini bilir.
Şema Numaraları
Sınıfın yöntemiyle karşılaşıldığında IMPLEMENT_SERIAL
sınıfına atanan şema numarası, sınıf uygulamasının "sürümüdür". Şema, belirli bir nesnenin kalıcı hale getirilme sayısına (genellikle nesne sürümü olarak adlandırılır) değil, sınıfın uygulanmasına başvurur.
Zaman içinde aynı sınıfın birkaç farklı uygulamasını korumak istiyorsanız, nesnenizin Serialize
yöntem uygulamasını düzelttikça şemayı artırmak, uygulamanın eski sürümlerini kullanarak depolanan nesneleri yükleyebilen kod yazmanıza olanak tanır.
yöntemi, CArchive::ReadObject
kalıcı depoda bellekteki sınıf açıklamasının şema numarasından farklı bir şema numarasıyla karşılaştığında bir CArchiveException oluşturur. Bu özel durumdan kurtarmak kolay değildir.
Bu özel durumun atılmasını korumak için şema sürümünüzü (bit düzeyinde VEYA) ile birlikte kullanabilirsinizVERSIONABLE_SCHEMA
. kullanarak VERSIONABLE_SCHEMA
kodunuz CArchive::GetObjectSchema'dan dönüş değerini denetleyerek işlevinde Serialize
uygun eylemi gerçekleştirebilir.
Serileştirmeyi Doğrudan Çağırma
Çoğu durumda genel nesne arşiv şemasının WriteObject
ek yükü ve ReadObject
gerekli değildir. Bu, verileri CDocument'a seri hale getirmenin yaygın bir örneğidir. Bu durumda, Serialize
yöntemi CDocument
ayıklama veya ekleme işleçleriyle değil doğrudan çağrılır. Belgenin içeriği de daha genel nesne arşiv düzenini kullanabilir.
Çağrının Serialize
doğrudan aşağıdaki avantajları ve dezavantajları vardır:
Nesne seri hale getirilmeden önce veya sonra arşive ek bayt eklenmez. Bu yalnızca kaydedilen verileri küçültmekle kalmaz, aynı zamanda herhangi bir dosya biçimini işleyebilen yordamlar uygulamanıza
Serialize
da olanak tanır.MFC ayarlı
WriteObject
olduğundan veReadObject
uygulamaları ve ilgili koleksiyonlar, başka bir amaçla daha genel nesne arşiv düzenine ihtiyacınız olmadığı sürece uygulamanıza bağlanmayacak.Kodunuzun eski şema numaralarından kurtarılması gerekmez. Bu, belge serileştirme kodunuzu şema numaralarını, dosya biçimi sürüm numaralarını veya veri dosyalarınızın başında kullandığınız tanımlayıcı sayıları kodlamaktan sorumlu hale getirir.
için doğrudan çağrıyla
Serialize
seri hale getirilmiş herhangi bir nesnenin, sürümün bilinmediğini belirten (UINT)-1 dönüş değerini kullanmamasıCArchive::GetObjectSchema
veya işlemesi gerekir.
Serialize
Doğrudan belgenizde çağrıldığından, belgenin alt nesnelerinin üst belge başvurularını arşivlemesi genellikle mümkün değildir. Bu nesnelere kapsayıcı belgelerine açıkça bir işaretçi verilmelidir veya bu arka işaretçiler arşivlemeden önce işaretçiyi bir PID'ye eşlemek CDocument
için CArchive::MapObject işlevini kullanmanız gerekir.
Daha önce belirtildiği gibi, doğrudan aradığınızda Serialize
sürüm ve sınıf bilgilerini kendiniz kodlamanız ve daha sonra eski dosyalarla geriye dönük uyumluluğu korurken biçimi değiştirmenize olanak tanır. İşlev, CArchive::SerializeClass
bir nesneyi doğrudan seri hale getirmeden önce veya temel sınıfı çağırmadan önce açıkça çağrılabilir.