Aracılığıyla paylaş


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 CArchivekapsamdan çı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 WriteObjecttersidir. gibiWriteObject, ReadObject kullanıcı kodu tarafından doğrudan çağrılmaz; kullanıcı kodu beklenen CRuntimeClassile ç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_SCHEMAkodunuz 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 ve ReadObject 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.

Ayrıca bkz.

Sayıya Göre Teknik Notlar
Kategoriye Göre Teknik Notlar