シリアル化(Serialization)

12203 ワード

シリアル化(Serialization)転載自:VC知識ベース著者:アヨンシリアル化はマイクロソフトが提供したオブジェクトに対するファイルI/Oのメカニズムであり、フレームワーク/ドキュメント(Document)/ビュー(View)モードでよく応用されている.シリアル化とは何か、オブジェクトをシリアル化する方法、シリアル化機能をどのように使用するかなどの問題がよく分からない人が多い.本論文では,シリアル化について簡単な解釈を試みた.本人はシリアル化機能もあまり使用していないので、不足点はご了承ください.MFCフレーム/ドキュメント/ビュー構造におけるファイル読み書き
CFIleは、MFCクラスライブラリ内のすべてのファイルクラスのベースクラスです.すべてのMFCが提供するファイルI/O機能はこのクラスに関連している.多くの場合、CFIle::Write/WriteHugeを直接呼び出してファイルを書き、CFIle::Read/ReadHugeを呼び出してファイルを読むのが好きです.このようなファイルI/Oは、MFCを使用しないファイルI/Oとあまり変わらず、従来のANSI CのファイルI/Oともあまり変わらず、呼び出されたAPIの違いにほかならない.
C++の勉強を始めると、cin/coutに詳しいに違いありません.この2つのオブジェクトは非常に明瞭な<>演算子を使用してI/Oを行い、その使用形式は以下の通りです.
        
        //    1
        int i;
        cin >> i;
        //here do something to object i
        cout << i;

このようにI/Oの利点を行う場合、演算子リロード機能を使用すると、オブジェクトの特定のタイプを区別することなく、一連のオブジェクトの読み書きを1つの文で完了できます.MFCはクラスCArchiveを提供し、演算子<>のリロードを実現し、前のcinとcoutの方式でファイルI/Oを行うことを望んでいる.CFIleクラスとの連携により,int/floatなどの単純なタイプのファイルの読み書きだけでなく,シーケンス化可能なオブジェクト(Serializable Objects,この概念は後述)のファイルの読み書きも実現した.
一般に、CArchiveを使用してオブジェクトを読み込む手順は次のとおりです.
        
        //    2
        //             
        CFile file;
        CFileException fe;
        //        
        if(!file.Open(filename,CFile::modeRead,&fe))
        {
                fe.ReportError();
                return;
        }
        
        //  CArchive   
        CArchive ar(&file,CArchive::load);
        ar >> obj1>>obj2>>obj3...>>objn;
        ar.Flush();
        //   ,     
        ar.Close();
        file.Close();

CArchiveを使用してオブジェクトを書き込みます.
        
        //    3
        //             
        CFile file;
        CFileException fe;
        //        
        if(!file.Open(filename,CFile::modeWrite|CFile::modeCreate,&fe))
        {
                fe.ReportError();
                return;
        }
        
        //  CArchive   
        CArchive ar(&file,CArchive::load);
        ar << obj1< 
   
  

, , , 。 MFC / / , , , 。 CDocument CObject Serilize , 。

/ (ID_FILE_OPEN) ,CWinApp OnFileOpen , (MDI)/ (SDI) 、 , CDocument::OnOpenDocument ,CDocument::OnOpenDocument :

        
        //    4
        BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
        {
            if (IsModified())
        		TRACE0("Warning: OnOpenDocument replaces an unsaved document./n");
        
        	CFileException fe;
        	CFile* pFile = GetFile(lpszPathName,
        		CFile::modeRead|CFile::shareDenyWrite, &fe);
        	if (pFile == NULL)
        	{
        		ReportSaveLoadException(lpszPathName, &fe,
        			FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
        		return FALSE;
        	}
        
        	DeleteContents();
        	SetModifiedFlag();  // dirty during de-serialize
        
        	CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
        	loadArchive.m_pDocument = this;
        	loadArchive.m_bForceFlat = FALSE;
        	TRY
        	{
        		CWaitCursor wait;
        		if (pFile->GetLength() != 0)
        			Serialize(loadArchive);     // load me
        		loadArchive.Close();
        		ReleaseFile(pFile, FALSE);
        	}
        	CATCH_ALL(e)
        	{
        		ReleaseFile(pFile, TRUE);
        		DeleteContents();   // remove failed contents
        
        		TRY
        		{
        			ReportSaveLoadException(lpszPathName, e,
        				FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
        		}
        		END_TRY
        		DELETE_EXCEPTION(e);
        		return FALSE;
        	}
        	END_CATCH_ALL
        
        	SetModifiedFlag(FALSE);     // start off with unmodified
        
        	return TRUE;
        }

に、ユーザがメニューファイル/ファイル (ID_FILE_SAVE)を するか、ファイル/ を けて するか…(ID_FILE_SAVEAS)の 、CWinApp::OnFileSaveとCWinApp::OnFileSaveAsが にCDocument::OnSaveDocumentを び し、この は のように されます.
        
        //    5
        BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)
        {
        	CFileException fe;
        	CFile* pFile = NULL;
        	pFile = GetFile(lpszPathName, CFile::modeCreate |
        		CFile::modeReadWrite | CFile::shareExclusive, &fe);
        
        	if (pFile == NULL)
        	{
        		ReportSaveLoadException(lpszPathName, &fe,
        			TRUE, AFX_IDP_INVALID_FILENAME);
        		return FALSE;
        	}
        
        	CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete);
        	saveArchive.m_pDocument = this;
        	saveArchive.m_bForceFlat = FALSE;
        	TRY
        	{
        		CWaitCursor wait;
        		Serialize(saveArchive);     // save me
        		saveArchive.Close();
        		ReleaseFile(pFile, FALSE);
        	}
        	CATCH_ALL(e)
        	{
        		ReleaseFile(pFile, TRUE);
        
        		TRY
        		{
        			ReportSaveLoadException(lpszPathName, e,
        				TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
        		}
        		END_TRY
        		DELETE_EXCEPTION(e);
        		return FALSE;
        	}
        	END_CATCH_ALL
        
        	SetModifiedFlag(FALSE);     // back to unmodified
        
        	return TRUE;        // success
        }

の2つのコードから、ファイルの み りと き みの は に じであり、 にはCObject::Serialize を び してドキュメント の み りと き みを していることがわかります( のsave meとload meを ).AppWizardで に されたMDIとSDIの 、システムはこの のリロード を に し、デフォルトの は のとおりです.
        
        //    6
        void CMyDoc::Serialize(CArchive& ar)
        {
        	if (ar.IsStoring())
        	{
        		// TODO: add storing code here
        	}
        	else
        	{
        		// TODO: add loading code here
        	}
        }

VCに しい が、すべてのコードを で するのが きな (もちろん の も ありません)、 が したCDocument クラスもこのデフォルトのSerialize を する があります.そうしないと、システムはファイルの み き にCObject::Serializeを び すしかありません.この は もしません.もちろん、 のオブジェクトのファイルの /ロードも しません.もちろん、ユーザーはID_をキャプチャすることもできます.FILE_OPENなどのメニューは、 のファイルの み き を しますが、このようなコードは に わしくなり、 みにくくなります.
CMyDoc::Serialize に ります.この はarオブジェクトの により, ファイルを んでいるか いているかを する.AppWizardはあなたのドキュメントが をしているのか からないので、 のファイルの み きコードを しません.あなたのドキュメントに3つのオブジェクトがあるとしますm_Obj_a,m_Obj_b,m_Obj_c,では のコードは:
        
        //    7
        void CMyDoc::Serialize(CArchive& ar)
        {
            	if (ar.IsStoring())
            	{
            		ar << m_Obj_a << m_Obj_b << m_Obj_c;
            	}
            	else
            	{
            		ar >> m_Obj_a >> m_Obj_b >> m_Obj_c;
            	}
        }

シリアル オブジェクト(Serializable Object)
サンプルコード7でファイルI/Oを うための は、m_Obj_aなどのオブジェクトはシリアル なオブジェクトでなければならない.シリアル なオブジェクトの は のとおりです.
  • このクラスはCObjectから している)
  • クラスはSerialize
  • を した
  • このクラスは にDECLARE_を しています.SERIALマクロ
  • クラスの ファイルにIMPLEMENT_を SERIALマクロ
  • このクラスにはパラメータを たない があり、またはパラメータを つ のすべてのパラメータがデフォルトのパラメータ
  • を しています.
    ここで、シリアル なオブジェクト には なタイプは まれておらず、 なタイプの 、CArchiveは に <>のリロードを するため、シリアル を して み きを うことができる.
    CObjectクラスから
    シリアル には、オブジェクトがCObjectから するか、または1つのCObjectの クラスから する があります.この は で,ほとんどのクラス(CStringを く)がCObjectから しているため,MFCクラスから されたクラスに してこの を たす. のデータクラスに して、そのベースクラスをCObjectとして してこの を たすことができます.
    Serialize の
    Serialize はオブジェクトが にデータを する であり、シリアル の である.その はCMyDoc::Serializeと に,CArchive::IsStoringとCArchive::IsLoadingを いて の を し,<>を してオブジェクトの と み りを う.
    DECLARE_の SERIALマクロ
    DECLARE_SERIALマクロにはDECLARE_が まれていますDYNAMICとDECLARE_DYNCREATE は、クラスのCRuntimeClass を し、デフォルトのoperator>>リロードを します.このマクロが すると,CArchiveはReadObjectとWriteObjectを してオブジェクトI/Oを い,あらかじめタイプを らずにファイルからオブジェクトを み ることができる.
    IMPLEMENT_の SERIAL
    DECLARE_SERIALマクロとIMPLEMENT_SERIALマクロはペアで する があります.そうしないとDECLARE_SERIALマクロで されたエンティティは できず、 に エラーが します.
    デフォルトコンストラクタ
    これはCRuntimeClass::CreateObjectのオブジェクトに する です.
  • は、ReadObject/WriteObjectと を せずにSerialize のみでオブジェクトを み きする 、 のシリアル な は であり、Serialize を すればよい.
  • のクラスでは、シリアル が されていない は、リロードされた operator<>を することで できます.


  • なグラフィック をサポートするジオメトリ 、 プログラムが であると します.ここでは, なグラフィックシステムの を するのではなく, オブジェクトの とロードのみを する.
    ベースクラスCPicture
    グラフィックオブジェクトはCPictureから し、このクラスはシリアル を しているが、 には コードは の りである.
            //   picture.h
    
            #if !defined(__PICTURE_H__)
            #define __PICTURE_H__
            
            #if _MSC_VER > 1000
            #pragma once
            #endif // _MSC_VER > 1000
            
            const int TYPE_UNKNOWN = -1;
            class CPicture:public CObject
            {
                int m_nType;//    
                DECLARE_SERIAL(CPicture)
            public:
                CPicture(int m_nType=TYPE_UNKNOWN):m_nType(m_nType){};
                int GetType()const {return m_nType;};
                virtual void Draw(CDC * pDC);
                void Serialize(CArchive & ar);
            };
            #endif
    
            //cpp  picture.cpp
            #include "stdafx.h"
            #include "picture.h"
            
            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif
            
            void CPicture::Draw(CDC * pDC)
            {
               //         ,      
            }
            
            void CPicture::Serialize(CArchive & ar)
            {
                if(ar.IsLoading())
                {
                    ar << m_nType;
                }else{
                    ar >> m_nType;
                
                }
            }
    

    :CRuntimeClassはこのオブジェクトをインスタンス する があるため、Draw には はありませんが、このクラスは な として されていません.
    CDocument クラスでのオブジェクトの とファイルI/Oプロセス
    を するため、CDocumentクラス クラスでは、MFCが するテンプレートクラスCPtrListを いてオブジェクトを する.オブジェクトは のように されます.
            protected:
                CTypedPtrList m_listPictures;
    

    CTypedPtrListもCPtrListもSerialize を していないため、ar<>m_ListPicturesはオブジェクトをシーケンス するため、CPictureDocのSerialize は のように する があります.
            void CTsDoc::Serialize(CArchive& ar)
            {
                POSITION pos;
            	if (ar.IsStoring())
            	{
            		// TODO: add storing code here
                    pos = m_listPictures.GetHeadPosition();
                    while(pos != NULL)
                    {
                        ar << m_listPictures.GetNext (pos);
                    }
            	}
            	else
            	{
            		// TODO: add loading code here
                    RemoveAll();
                    CPicture * pPicture;
                    do{
                        try
                        {
                            ar >> pPicture;
                            TRACE("Read Object %d/n",pPicture->GetType ());
                            m_listPictures.AddTail(pPicture);
                        }
                        catch(CException * e)
                        {
                            e->Delete ();
                            break;
                        }
                    }while(pPicture != NULL);
            	}
                m_pCurrent = NULL;
                SetModifiedFlag(FALSE);
            }
    

    クラスのシリアル の
    ジオメトリプログラムは、 、 、 、 などのグラフィックをサポートし、クラスCLine、CRectangle、CTriangle、CEllipseで されます.クラスCLineを に、シリアル を する:
  • CPictureから CLineは、CLineクラス に のメンバー を します:
        CPoint m_ptStart,m_ptEnd;
    
  • この の の には、
        DECLARE_SERIAL(CLine)
    
  • というマクロが される.
  • Serialize
        void CLine::Serialize(CArchive & ar)
        {
            CPicture::Serialize(ar);
            if(ar.IsLoading())
            {
                ar>>m_ptStart.x>>m_ptStart.y>>m_ptEnd.x>>m_ptEnd.y;
            }else{
                ar< 
  • CPPファイルに
        IMPLEMENT_SERIAL(CLine,CPicture,TYPE_LINE);
    

  • このように されたCLineはシリアル を し、 のグラフィッククラスは に することができる.