• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            C++ Programmer's Cookbook

            {C++ 基礎} {C++ 高級} {C#界面,C++核心算法} {設計模式} {C#基礎}

            CArchive原理

             

            一.概述

            CArchive使用了緩沖區(qū),即一段內(nèi)存空間作為臨時數(shù)據(jù)存儲地,對CArchive的讀寫都先依次排列到此緩沖區(qū),當緩沖區(qū)滿或用戶要求時,將此段整理后的數(shù)據(jù)讀寫到指定的存儲煤質。
            當建立CArchive對象時,應指定其模式是用于緩沖區(qū)讀,還是用于緩沖區(qū)寫。
            可以這樣理解,CArchive對象相當于鐵路的貨運練調度站,零散的貨物被收集,當總量到達火車運量的時候,由火車裝運走。
            當接到火車的貨物時,則貨物由被分散到各自的貨主。與貨運不同的是,交貨、取貨是按時間循序執(zhí)行的,而不是憑票據(jù)。因此必須保證送貨的和取貨的貨主按同樣的循序去存或取。
            對于大型的貨物,則是拆散成火車單位,運走,取貨時,依次取各部分,組裝成原物。

            二.內(nèi)部數(shù)據(jù)
            緩沖區(qū)指針 BYTE* m_lpBufStart,指向緩沖區(qū),這個緩沖區(qū)有可能是底層CFile(如派生類CMemFile)對象提供的,但一般是CArchive自己建立的。
            緩沖區(qū)尾部指針 BYTE* m_lpBufMax;
            緩沖區(qū)當前位置指針 BYTE* m_lpBufCur;
            初始化時,如果是讀模式,當前位置在尾部,如果是寫模式,當前位置在頭部:

            m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
            三.基本數(shù)據(jù)讀寫
            對于基本的數(shù)據(jù)類型,例如字節(jié)、雙字等,可以直接使用">>"、"<<"符號進行讀出、寫入。

            //操作符定義捕:
             
            //插入操作
            CArchive& operator<<(BYTE by);
            CArchive& operator<<(WORD w);
            CArchive& operator<<(LONG l);
            CArchive& operator<<(DWORD dw);
            CArchive& operator<<(float f);
            CArchive& operator<<(double d);
            CArchive& operator<<(int i);
            CArchive& operator<<(short w);
            CArchive& operator<<(char ch);
            CArchive& operator<<(unsigned u);

            //提取操作
            CArchive& operator>>(BYTE& by);
            CArchive& operator>>(WORD& w);
            CArchive& operator>>(DWORD& dw);
            CArchive& operator>>(LONG& l);
            CArchive& operator>>(float& f);
            CArchive& operator>>(double& d);

            CArchive& operator>>(int& i);
            CArchive& operator>>(short& w);
            CArchive& operator>>(char& ch);
            CArchive& operator>>(unsigned& u);
            下面以雙字為例,分析原碼
            雙字的插入(寫)

            CArchive& CArchive::operator<<(DWORD dw)
            {
             if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax) //緩沖區(qū)空間不夠
              Flush();  //緩沖區(qū)內(nèi)容提交到實際存儲煤質。

             if (!(m_nMode & bNoByteSwap))
              _AfxByteSwap(dw, m_lpBufCur);  //處理字節(jié)順序
             else
              *(DWORD*)m_lpBufCur = dw;      //添入緩沖區(qū)

             m_lpBufCur += sizeof(DWORD);     //移動當前指針
             return *this;
            }

            雙字的提取(讀)
            CArchive& CArchive::operator>>(DWORD& dw)
            {
             if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax) //緩沖區(qū)要讀完了
              FillBuffer(sizeof(DWORD) - (UINT)(m_lpBufMax - m_lpBufCur));  //重新讀入內(nèi)容到緩沖區(qū)

             dw = *(DWORD*)m_lpBufCur;  //讀取雙字
             m_lpBufCur += sizeof(DWORD); //移動當前位置指針

             if (!(m_nMode & bNoByteSwap))
              _AfxByteSwap(dw, (BYTE*)&dw);  //處理字節(jié)順序
             return *this;
            }

            四.緩沖區(qū)的更新

            以上操作中,當緩沖區(qū)將插入滿或緩沖區(qū)將提取空時,都將對緩沖區(qū)進行更新處理。

            緩沖區(qū)將插入滿時調用Flush();
            void CArchive::Flush()
            {
             ASSERT_VALID(m_pFile);
             ASSERT(m_bDirectBuffer || m_lpBufStart != NULL);
             ASSERT(m_bDirectBuffer || m_lpBufCur != NULL);
             ASSERT(m_lpBufStart == NULL ||
              AfxIsValidAddress(m_lpBufStart, m_lpBufMax - m_lpBufStart, IsStoring()));
             ASSERT(m_lpBufCur == NULL ||
              AfxIsValidAddress(m_lpBufCur, m_lpBufMax - m_lpBufCur, IsStoring()));

             if (IsLoading())
             {
              // unget the characters in the buffer, seek back unused amount
              if (m_lpBufMax != m_lpBufCur)
               m_pFile-> Seek(-(m_lpBufMax - m_lpBufCur), CFile::current);
              m_lpBufCur = m_lpBufMax;    // 指向尾
             }
             else   //寫模式
             {
              if (!m_bDirectBuffer)
              {
               // 內(nèi)容寫入到文件
               if (m_lpBufCur != m_lpBufStart)
                m_pFile-> Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);
              }
              else
              {
               //如果是直接針對內(nèi)存區(qū)域的的(例如CMemFile中) (只需移動相關指針,指向新的一塊內(nèi)存)
               if (m_lpBufCur != m_lpBufStart)
                m_pFile-> GetBufferPtr(CFile::bufferCommit, m_lpBufCur - m_lpBufStart);
               // get next buffer
               VERIFY(m_pFile-> GetBufferPtr(CFile::bufferWrite, m_nBufSize,
                (void**)&m_lpBufStart, (void**)&m_lpBufMax) == (UINT)m_nBufSize);
               ASSERT((UINT)m_nBufSize == (UINT)(m_lpBufMax - m_lpBufStart));
              }
              m_lpBufCur = m_lpBufStart; //指向緩沖區(qū)首
             }
            }
            緩沖區(qū)將提取空,會調用FillBuffer。 nBytesNeeded為當前剩余部分上尚有用的字節(jié)
            void CArchive::FillBuffer(UINT nBytesNeeded)
            {
             ASSERT_VALID(m_pFile);
             ASSERT(IsLoading());
             ASSERT(m_bDirectBuffer || m_lpBufStart != NULL);
             ASSERT(m_bDirectBuffer || m_lpBufCur != NULL);
             ASSERT(nBytesNeeded > 0);
             ASSERT(nBytesNeeded <= (UINT)m_nBufSize);
             ASSERT(m_lpBufStart == NULL ||
              AfxIsValidAddress(m_lpBufStart, m_lpBufMax - m_lpBufStart, FALSE));
             ASSERT(m_lpBufCur == NULL ||
              AfxIsValidAddress(m_lpBufCur, m_lpBufMax - m_lpBufCur, FALSE));

             UINT nUnused = m_lpBufMax - m_lpBufCur;
             ULONG nTotalNeeded = ((ULONG)nBytesNeeded) + nUnused;

             // 從文件中讀取
             if (!m_bDirectBuffer)
             {
              ASSERT(m_lpBufCur != NULL);
              ASSERT(m_lpBufStart != NULL);
              ASSERT(m_lpBufMax != NULL);

              if (m_lpBufCur > m_lpBufStart)
              {
               //保留剩余的尚未處理的部分,將它們移動到頭
               if ((int)nUnused > 0)
               {
                memmove(m_lpBufStart, m_lpBufCur, nUnused);
                m_lpBufCur = m_lpBufStart;
                m_lpBufMax = m_lpBufStart + nUnused;
               }

               // read to satisfy nBytesNeeded or nLeft if possible
               UINT nRead = nUnused;
               UINT nLeft = m_nBufSize-nUnused;
               UINT nBytes;
               BYTE* lpTemp = m_lpBufStart + nUnused;
               do
               {
                nBytes = m_pFile-> Read(lpTemp, nLeft);
                lpTemp = lpTemp + nBytes;
                nRead += nBytes;
                nLeft -= nBytes;
               }
               while (nBytes > 0 && nLeft > 0 && nRead < nBytesNeeded);

               m_lpBufCur = m_lpBufStart;
               m_lpBufMax = m_lpBufStart + nRead;
              }
             }
             else
             {
              // 如果是針對內(nèi)存區(qū)域(CMemFile),移動相關指針,指向新的一塊內(nèi)存
              if (nUnused != 0)
               m_pFile-> Seek(-(LONG)nUnused, CFile::current);
              UINT nActual = m_pFile-> GetBufferPtr(CFile::bufferRead, m_nBufSize,
               (void**)&m_lpBufStart, (void**)&m_lpBufMax);
              ASSERT(nActual == (UINT)(m_lpBufMax - m_lpBufStart));
              m_lpBufCur = m_lpBufStart;
             }

             // not enough data to fill request?
             if ((ULONG)(m_lpBufMax - m_lpBufCur) < nTotalNeeded)
              AfxThrowArchiveException(CArchiveException::endOfFile);
            }

            五.指定長度數(shù)據(jù)段落的讀寫

            以下分析
            UINT Read(void* lpBuf, UINT nMax); 讀取長度為nMax的數(shù)據(jù)
            void Write(const void* lpBuf, UINT nMax); 寫入指定長度nMax的數(shù)據(jù)
            對于大段數(shù)據(jù)的讀寫,先使用當前緩沖區(qū)中的內(nèi)容或空間讀取或寫入,若這些空間夠用了,則結束。
            否則,從剩余的數(shù)據(jù)中找出最大的緩沖區(qū)整數(shù)倍大小的一塊數(shù)據(jù),直接讀寫到存儲煤質(不反復使用緩沖區(qū))。
            剩余的余數(shù)部分,再使用緩沖區(qū)讀寫。
            (說明:緩沖區(qū)讀寫的主要目的是將零散的數(shù)據(jù)以緩沖區(qū)大小為尺度來處理。對于大型數(shù)據(jù),其中間的部分,不是零散的數(shù)據(jù),使用緩沖區(qū)已經(jīng)沒有意思,故直接讀寫)
            ①讀取

            UINT CArchive::Read(void* lpBuf, UINT nMax)
            {
             ASSERT_VALID(m_pFile);
             if (nMax == 0)
              return 0;

             UINT nMaxTemp = nMax;  //還需要讀入的長度,讀入一部分,就減相應數(shù)值,直到此數(shù)值變?yōu)榱?br> 
             //處理當前緩沖區(qū)中剩余部分。
             //如果要求讀入字節(jié)小于緩沖區(qū)中剩余部分,則第一部分為要求讀入的字節(jié)數(shù),
             //否則讀入全部剩余部分 
             UINT nTemp = min(nMaxTemp, (UINT)(m_lpBufMax - m_lpBufCur));  
             memcpy(lpBuf, m_lpBufCur, nTemp);
             m_lpBufCur += nTemp;
             lpBuf = (BYTE*)lpBuf + nTemp; //移動讀出內(nèi)容所在區(qū)域的指針
             nMaxTemp -= nTemp;

             //當前緩沖區(qū)中剩余部分不夠要求讀入的長度。
             //還有字節(jié)需要讀,則需要根據(jù)需要執(zhí)行若干次填充緩沖區(qū),讀出,直到讀出指定字節(jié)。
             if (nMaxTemp != 0) 
             {
              //計算出去除尾數(shù)部分的字節(jié)大小(整數(shù)個緩沖區(qū)大小)
              //對于這些部分,字節(jié)從文件對象中讀出,放到輸出緩沖區(qū)
              nTemp = nMaxTemp - (nMaxTemp % m_nBufSize); 
              UINT nRead = 0;

              UINT nLeft = nTemp;
              UINT nBytes;
              do
              {
               nBytes = m_pFile-> Read(lpBuf, nLeft); //要求讀入此整數(shù)緩沖區(qū)部分大小
               lpBuf = (BYTE*)lpBuf + nBytes;
               nRead += nBytes;
               nLeft -= nBytes;
              }
              while ((nBytes > 0) && (nLeft > 0)); 知道讀入了預定大小,或到達文件尾

              nMaxTemp -= nRead;

              if (nRead == nTemp) //讀入的字節(jié)等于讀入的整數(shù)倍部分  該讀最后的余數(shù)部分了
              {
               // 建立裝有此最后余數(shù)部分的內(nèi)容的CArchive的工作緩沖區(qū)。
               if (!m_bDirectBuffer)
               {
                UINT nLeft = max(nMaxTemp, (UINT)m_nBufSize);
                UINT nBytes;
                BYTE* lpTemp = m_lpBufStart;
                nRead = 0;
                do
                {
                 nBytes = m_pFile-> Read(lpTemp, nLeft);  //從文件中讀入到CArchive緩沖區(qū)
                 lpTemp = lpTemp + nBytes;
                 nRead += nBytes;
                 nLeft -= nBytes;
                }
                while ((nBytes > 0) && (nLeft > 0) && nRead < nMaxTemp);

                m_lpBufCur = m_lpBufStart;
                m_lpBufMax = m_lpBufStart + nRead;
               }
               else
               {
                nRead = m_pFile-> GetBufferPtr(CFile::bufferRead, m_nBufSize,
                 (void**)&m_lpBufStart, (void**)&m_lpBufMax);
                ASSERT(nRead == (UINT)(m_lpBufMax - m_lpBufStart));
                m_lpBufCur = m_lpBufStart;
               }

               //讀出此剩余部分到輸出
               nTemp = min(nMaxTemp, (UINT)(m_lpBufMax - m_lpBufCur));
               memcpy(lpBuf, m_lpBufCur, nTemp);
               m_lpBufCur += nTemp;
               nMaxTemp -= nTemp;
              }
              
             }
             return nMax - nMaxTemp;
            }

            ②保存,寫入
            void CArchive::Write(const void* lpBuf, UINT nMax)
            {
             if (nMax == 0)
              return;
             
             //讀入可能的部分到緩沖區(qū)當前的剩余部分 
             UINT nTemp = min(nMax, (UINT)(m_lpBufMax - m_lpBufCur));
             memcpy(m_lpBufCur, lpBuf, nTemp);
             m_lpBufCur += nTemp;
             lpBuf = (BYTE*)lpBuf + nTemp;
             nMax -= nTemp;

             if (nMax > 0)  //還有未寫入的部分
             {
              Flush();    //將當前緩沖區(qū)寫入到存儲煤質

              //計算出整數(shù)倍緩沖區(qū)大小的字節(jié)數(shù)
              nTemp = nMax - (nMax % m_nBufSize);
              m_pFile-> Write(lpBuf, nTemp);  //直接寫到文件
              lpBuf = (BYTE*)lpBuf + nTemp;
              nMax -= nTemp;


              //剩余部分添加到緩沖區(qū)
              if (m_bDirectBuffer)
              {
               // sync up direct mode buffer to new file position
               VERIFY(m_pFile-> GetBufferPtr(CFile::bufferWrite, m_nBufSize,
                (void**)&m_lpBufStart, (void**)&m_lpBufMax) == (UINT)m_nBufSize);
               ASSERT((UINT)m_nBufSize == (UINT)(m_lpBufMax - m_lpBufStart));
               m_lpBufCur = m_lpBufStart;
              }

              // copy remaining to active buffer
              ASSERT(nMax < (UINT)m_nBufSize);
              ASSERT(m_lpBufCur == m_lpBufStart);
              memcpy(m_lpBufCur, lpBuf, nMax);
              m_lpBufCur += nMax;
             }
            }

            六.字符串的讀寫

            ①CArchive提供的WriteString和ReadString

            字符串寫
            void CArchive::WriteString(LPCTSTR lpsz)
            {
             ASSERT(AfxIsValidString(lpsz));
             Write(lpsz, lstrlen(lpsz) * sizeof(TCHAR));  //調用Write,將字符串對應的一段數(shù)據(jù)寫入
            }

            字符串讀(讀取一行字符串)
            LPTSTR CArchive::ReadString(LPTSTR lpsz, UINT nMax)
            {
             // if nMax is negative (such a large number doesn''t make sense given today''s
             // 2gb address space), then assume it to mean "keep the newline".
             int nStop = (int)nMax < 0 ? -(int)nMax : (int)nMax;
             ASSERT(AfxIsValidAddress(lpsz, (nStop+1) * sizeof(TCHAR)));

             _TUCHAR ch;
             int nRead = 0;

             TRY
             {
              while (nRead < nStop)
              {
               *this >> ch;  //讀出一個字節(jié)

               // stop and end-of-line (trailing ''\n'' is ignored)  遇換行—回車
               if (ch == ''\n'' || ch == ''\r'')
               {
                if (ch == ''\r'')
                 *this >> ch;
                // store the newline when called with negative nMax
                if ((int)nMax != nStop)
                 lpsz[nRead++] = ch;
                break;
               }
               lpsz[nRead++] = ch;
              }
             }
             CATCH(CArchiveException, e)
             {
              if (e-> m_cause == CArchiveException::endOfFile)
              {
               DELETE_EXCEPTION(e);
               if (nRead == 0)
                return NULL;
              }
              else
              {
               THROW_LAST();
              }
             }
             END_CATCH

             lpsz[nRead] = ''\0'';
             return lpsz;
            }

            ReadString到CString對象,可以多行字符
            BOOL CArchive::ReadString(CString& rString)
            {
             rString = &afxChNil;    // empty string without deallocating
             const int nMaxSize = 128;
             LPTSTR lpsz = rString.GetBuffer(nMaxSize);
             LPTSTR lpszResult;
             int nLen;
             for (;;)
             {
              lpszResult = ReadString(lpsz, (UINT)-nMaxSize); // store the newline
              rString.ReleaseBuffer();

              // if string is read completely or EOF
              if (lpszResult == NULL ||
               (nLen = lstrlen(lpsz)) < nMaxSize ||
               lpsz[nLen-1] == ''\n'')
              {
               break;
              }

              nLen = rString.GetLength();
              lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen;
             }

             // remove ''\n'' from end of string if present
             lpsz = rString.GetBuffer(0);
             nLen = rString.GetLength();
             if (nLen != 0 && lpsz[nLen-1] == ''\n'')
              rString.GetBufferSetLength(nLen-1);

             return lpszResult != NULL;
            }

            ②使用CString對象的"<<"與">>"符讀寫字符串
            CString定義了輸入輸出符,可以象基本類型的數(shù)據(jù)一樣使用CArchive 的操作符定義

            friend CArchive& AFXAPI operator<<(CArchive& ar, const CString& string);
            friend CArchive& AFXAPI operator>>(CArchive& ar, CString& string);
            // CString serialization code
            // String format:
            //      UNICODE strings are always prefixed by 0xff, 0xfffe
            //      if < 0xff chars: len:BYTE, TCHAR chars
            //      if >= 0xff characters: 0xff, len:WORD, TCHAR chars
            //      if >= 0xfffe characters: 0xff, 0xffff, len:DWORD, TCHARs

            CArchive& AFXAPI operator<<(CArchive& ar, const CString& string)
            {
             // special signature to recognize unicode strings
            #ifdef _UNICODE
             ar << (BYTE)0xff;
             ar << (WORD)0xfffe;
            #endif

             if (string.GetData()-> nDataLength < 255)
             {
              ar << (BYTE)string.GetData()-> nDataLength;
             }
             else if (string.GetData()-> nDataLength < 0xfffe)
             {
              ar << (BYTE)0xff;
              ar << (WORD)string.GetData()-> nDataLength;
             }
             else
             {
              ar << (BYTE)0xff;
              ar << (WORD)0xffff;
              ar << (DWORD)string.GetData()-> nDataLength;
             }
             ar.Write(string.m_pchData, string.GetData()-> nDataLength*sizeof(TCHAR));
             return ar;
            }

            // return string length or -1 if UNICODE string is found in the archive
            AFX_STATIC UINT AFXAPI _AfxReadStringLength(CArchive& ar)
            {
             DWORD nNewLen;

             // attempt BYTE length first
             BYTE bLen;
             ar >> bLen;

             if (bLen < 0xff)
              return bLen;

             // attempt WORD length
             WORD wLen;
             ar >> wLen;
             if (wLen == 0xfffe)
             {
              // UNICODE string prefix (length will follow)
              return (UINT)-1;
             }
             else if (wLen == 0xffff)
             {
              // read DWORD of length
              ar >> nNewLen;
              return (UINT)nNewLen;
             }
             else
              return wLen;
            }

            CArchive& AFXAPI operator>>(CArchive& ar, CString& string)
            {
            #ifdef _UNICODE
             int nConvert = 1;   // if we get ANSI, convert
            #else
             int nConvert = 0;   // if we get UNICODE, convert
            #endif

             UINT nNewLen = _AfxReadStringLength(ar);
             if (nNewLen == (UINT)-1)
             {
              nConvert = 1 - nConvert;
              nNewLen = _AfxReadStringLength(ar);
              ASSERT(nNewLen != -1);
             }

             // set length of string to new length
             UINT nByteLen = nNewLen;
            #ifdef _UNICODE
             string.GetBufferSetLength((int)nNewLen);
             nByteLen += nByteLen * (1 - nConvert);  // bytes to read
            #else
             nByteLen += nByteLen * nConvert;    // bytes to read
             if (nNewLen == 0)
              string.GetBufferSetLength(0);
             else
              string.GetBufferSetLength((int)nByteLen+nConvert);
            #endif

             // read in the characters
             if (nNewLen != 0)
             {
              ASSERT(nByteLen != 0);

              // read new data
              if (ar.Read(string.m_pchData, nByteLen) != nByteLen)
               AfxThrowArchiveException(CArchiveException::endOfFile);

              // convert the data if as necessary
              if (nConvert != 0)
              {
            #ifdef _UNICODE
               CStringData* pOldData = string.GetData();
               LPSTR lpsz = (LPSTR)string.m_pchData;
            #else
               CStringData* pOldData = string.GetData();
               LPWSTR lpsz = (LPWSTR)string.m_pchData;
            #endif
               lpsz[nNewLen] = ''\0'';    // must be NUL terminated
               string.Init();   // don''t delete the old data
               string = lpsz;   // convert with operator=(LPWCSTR)
               CString::FreeData(pOldData);
              }
             }
             return ar;
            }

            七.CObject派生對象的讀寫
            MFC中多數(shù)類都從CObject類派生,CObject類與CArchive類有著良好的合作關系,能實現(xiàn)將對象序列化儲存到文件或其他媒介中去,或者讀取預先儲存的對象,動態(tài)建立對象等功能。

            ①CObject定義了針對CArvhive的輸入輸出操作符,可以向其他基本數(shù)據(jù)類型一樣使用"<<"、"<<"符號

            CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
             { ar.WriteObject(pOb); return ar; }
            CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb)
             { pOb = ar.ReadObject(NULL); return ar; }

            當使用這些符號時,實際上執(zhí)行的是CArchive的WriteObject和ReadObject成員
            ②WriteObject與ReadObject

            在WriteObject與ReadObject中先寫入或讀取運行時類信息(CRuntimeClas),再調用Serialze(..),按其中的代碼讀寫具體的對象數(shù)據(jù)。

            因此,只要在CObject派生類中重載Serilize()函數(shù),寫入具體的讀寫過程,就可以使對象具有存儲與創(chuàng)建能力。


            //將對象寫入到緩沖區(qū)
            void CArchive::WriteObject(const CObject* pOb)
            {
             DWORD nObIndex;
             // make sure m_pStoreMap is initialized
             MapObject(NULL);

             if (pOb == NULL)
             {
              // save out null tag to represent NULL pointer
              *this << wNullTag;
             }
             else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0)
              // assumes initialized to 0 map
             {
              // save out index of already stored object
              if (nObIndex < wBigObjectTag)
               *this << (WORD)nObIndex;
              else
              {
               *this << wBigObjectTag;
               *this << nObIndex;
              }
             }
             else
             {
              // write class of object first
              CRuntimeClass* pClassRef = pOb-> GetRuntimeClass();
              WriteClass(pClassRef);  //寫入運行類信息

              // enter in stored object table, checking for overflow
              CheckCount();
              (*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;

              // 調用CObject的Serialize成員,按其中的代碼寫入類中數(shù)據(jù)。
              ((CObject*)pOb)-> Serialize(*this);
             }
            }


            CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
            {

             // attempt to load next stream as CRuntimeClass
             UINT nSchema;
             DWORD obTag;
             //先讀入運行時類信息
             CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);

             // check to see if tag to already loaded object
             CObject* pOb;
             if (pClassRef == NULL)
             {
              if (obTag > (DWORD)m_pLoadArray-> GetUpperBound())
              {
               // tag is too large for the number of objects read so far
               AfxThrowArchiveException(CArchiveException::badIndex,
                m_strFileName);
              }

              pOb = (CObject*)m_pLoadArray-> GetAt(obTag);
              if (pOb != NULL && pClassRefRequested != NULL &&
                !pOb-> IsKindOf(pClassRefRequested))
              {
               // loaded an object but of the wrong class
               AfxThrowArchiveException(CArchiveException::badClass,
                m_strFileName);
              }
             }
             else
             {
              // 建立對象
              pOb = pClassRef-> CreateObject();
              if (pOb == NULL)
               AfxThrowMemoryException();

              // Add to mapping array BEFORE de-serializing
              CheckCount();
              m_pLoadArray-> InsertAt(m_nMapCount++, pOb);

              // Serialize the object with the schema number set in the archive
              UINT nSchemaSave = m_nObjectSchema;
              m_nObjectSchema = nSchema;
              pOb-> Serialize(*this); //調用CObject的Serialize,按其中代碼讀入對象數(shù)據(jù)。
              m_nObjectSchema = nSchemaSave;
              ASSERT_VALID(pOb);
             }

             return pOb;
            }


            ③運行時類信息的讀寫
            為了避免眾多重復的同類對象寫入重復的類信息,CArchive中使用CMap對象儲存和檢索類信息。


            void CArchive::WriteClass(const CRuntimeClass* pClassRef)
            {
             ASSERT(pClassRef != NULL);
             ASSERT(IsStoring());    // proper direction

             if (pClassRef-> m_wSchema == 0xFFFF)
             {
              TRACE1("Warning: Cannot call WriteClass/WriteObject for %hs.\n",
               pClassRef-> m_lpszClassName);
              AfxThrowNotSupportedException();
             }

             // make sure m_pStoreMap is initialized
             MapObject(NULL);

             // write out class id of pOb, with high bit set to indicate
             // new object follows

             // ASSUME: initialized to 0 map
             DWORD nClassIndex;
             if ((nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]) != 0)
             {
              // previously seen class, write out the index tagged by high bit
              if (nClassIndex < wBigObjectTag)
               *this << (WORD)(wClassTag | nClassIndex);
              else
              {
               *this << wBigObjectTag;
               *this << (dwBigClassTag | nClassIndex);
              }
             }
             else
             {
              // store new class
              *this << wNewClassTag;
              pClassRef-> Store(*this);

              // store new class reference in map, checking for overflow
              CheckCount();
              (*m_pStoreMap)[(void*)pClassRef] = (void*)m_nMapCount++;
             }
            }


            CRuntimeClass* CArchive::ReadClass(const CRuntimeClass* pClassRefRequested,
             UINT* pSchema, DWORD* pObTag)
            {
             ASSERT(pClassRefRequested == NULL ||
              AfxIsValidAddress(pClassRefRequested, sizeof(CRuntimeClass), FALSE));
             ASSERT(IsLoading());    // proper direction

             if (pClassRefRequested != NULL && pClassRefRequested-> m_wSchema == 0xFFFF)
             {
              TRACE1("Warning: Cannot call ReadClass/ReadObject for %hs.\n",
               pClassRefRequested-> m_lpszClassName);
              AfxThrowNotSupportedException();
             }

             // make sure m_pLoadArray is initialized
             MapObject(NULL);

             // read object tag - if prefixed by wBigObjectTag then DWORD tag follows
             DWORD obTag;
             WORD wTag;
             *this >> wTag;
             if (wTag == wBigObjectTag)
              *this >> obTag;
             else
              obTag = ((wTag & wClassTag) << 16) | (wTag & ~wClassTag);

             // check for object tag (throw exception if expecting class tag)
             if (!(obTag & dwBigClassTag))
             {
              if (pObTag == NULL)
               AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);

              *pObTag = obTag;
              return NULL;
             }

             CRuntimeClass* pClassRef;
             UINT nSchema;
             if (wTag == wNewClassTag)
             {
              // new object follows a new class id
              if ((pClassRef = CRuntimeClass::Load(*this, &nSchema)) == NULL)
               AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);

              // check nSchema against the expected schema
              if ((pClassRef-> m_wSchema & ~VERSIONABLE_SCHEMA) != nSchema)
              {
               if (!(pClassRef-> m_wSchema & VERSIONABLE_SCHEMA))
               {
                // schema doesn''t match and not marked as VERSIONABLE_SCHEMA
                AfxThrowArchiveException(CArchiveException::badSchema,
                 m_strFileName);
               }
               else
               {
                // they differ -- store the schema for later retrieval
                if (m_pSchemaMap == NULL)
                 m_pSchemaMap = new CMapPtrToPtr;
                ASSERT_VALID(m_pSchemaMap);
                m_pSchemaMap-> SetAt(pClassRef, (void*)nSchema);
               }
              }
              CheckCount();
              m_pLoadArray-> InsertAt(m_nMapCount++, pClassRef);
             }
             else
             {
              // existing class index in obTag followed by new object
              DWORD nClassIndex = (obTag & ~dwBigClassTag);
              if (nClassIndex == 0 || nClassIndex > (DWORD)m_pLoadArray-> GetUpperBound())
               AfxThrowArchiveException(CArchiveException::badIndex,
                m_strFileName);

              pClassRef = (CRuntimeClass*)m_pLoadArray-> GetAt(nClassIndex);
              ASSERT(pClassRef != NULL);

              // determine schema stored against objects of this type
              void* pTemp;
              BOOL bFound = FALSE;
              nSchema = 0;
              if (m_pSchemaMap != NULL)
              {
               bFound = m_pSchemaMap-> Lookup( pClassRef, pTemp );
               if (bFound)
                nSchema = (UINT)pTemp;
              }
              if (!bFound)
               nSchema = pClassRef-> m_wSchema & ~VERSIONABLE_SCHEMA;
               }

             // check for correct derivation
             if (pClassRefRequested != NULL &&
              !pClassRef-> IsDerivedFrom(pClassRefRequested))
             {
              AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
             }

             // store nSchema for later examination
             if (pSchema != NULL)
              *pSchema = nSchema;
             else
              m_nObjectSchema = nSchema;

             // store obTag for later examination
             if (pObTag != NULL)
              *pObTag = obTag;

             // return the resulting CRuntimeClass*
             return pClassRef;
            }

             

            posted on 2006-08-08 09:40 夢在天涯 閱讀(3097) 評論(1)  編輯 收藏 引用 所屬分類: CPlusPlus

            評論

            # re: CArchive原理 2006-09-14 15:18 愛飯盒

            好東東  回復  更多評論   

            公告

            EMail:itech001#126.com

            導航

            統(tǒng)計

            • 隨筆 - 461
            • 文章 - 4
            • 評論 - 746
            • 引用 - 0

            常用鏈接

            隨筆分類

            隨筆檔案

            收藏夾

            Blogs

            c#(csharp)

            C++(cpp)

            Enlish

            Forums(bbs)

            My self

            Often go

            Useful Webs

            Xml/Uml/html

            搜索

            •  

            積分與排名

            • 積分 - 1804430
            • 排名 - 5

            最新評論

            閱讀排行榜

            污污内射久久一区二区欧美日韩| 7777久久亚洲中文字幕| 国产精品激情综合久久| 国产韩国精品一区二区三区久久| 99精品久久久久久久婷婷| 日产久久强奸免费的看| 国产精品久久久久一区二区三区 | 国产99久久九九精品无码| 东京热TOKYO综合久久精品| 久久夜色精品国产噜噜麻豆| 97精品伊人久久久大香线蕉 | 亚洲级αV无码毛片久久精品| 亚洲一级Av无码毛片久久精品| 久久精品无码一区二区三区免费| 久久久久久久综合日本| 日本久久久久久久久久| 一本大道久久东京热无码AV| 香蕉久久夜色精品国产尤物| 久久国产色av免费看| 麻豆成人久久精品二区三区免费| 国产精品一久久香蕉国产线看观看| 69国产成人综合久久精品| 97久久精品人人澡人人爽| 人妻丰满?V无码久久不卡| 伊人久久精品无码二区麻豆| 久久久久亚洲AV无码麻豆| 一本大道加勒比久久综合| 亚洲va久久久久| 精品一区二区久久久久久久网站| 国产AV影片久久久久久| 香蕉久久夜色精品国产2020| 久久久久久亚洲AV无码专区| 精品国产热久久久福利| 精产国品久久一二三产区区别| 久久久久99精品成人片欧美 | 狠狠久久综合| 伊色综合久久之综合久久| 99久久99这里只有免费费精品| 久久播电影网| 狠狠狠色丁香婷婷综合久久五月| 久久久久无码中|