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

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

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

            //操作符定義捕:
             
            //插入操作
            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) //緩沖區空間不夠
              Flush();  //緩沖區內容提交到實際存儲煤質。

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

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

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

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

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

            四.緩沖區的更新

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

            緩沖區將插入滿時調用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)
              {
               // 內容寫入到文件
               if (m_lpBufCur != m_lpBufStart)
                m_pFile-> Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);
              }
              else
              {
               //如果是直接針對內存區域的的(例如CMemFile中) (只需移動相關指針,指向新的一塊內存)
               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; //指向緩沖區首
             }
            }
            緩沖區將提取空,會調用FillBuffer。 nBytesNeeded為當前剩余部分上尚有用的字節
            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
             {
              // 如果是針對內存區域(CMemFile),移動相關指針,指向新的一塊內存
              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);
            }

            五.指定長度數據段落的讀寫

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

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

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

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

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

              nMaxTemp -= nRead;

              if (nRead == nTemp) //讀入的字節等于讀入的整數倍部分  該讀最后的余數部分了
              {
               // 建立裝有此最后余數部分的內容的CArchive的工作緩沖區。
               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緩沖區
                 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;
             
             //讀入可能的部分到緩沖區當前的剩余部分 
             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();    //將當前緩沖區寫入到存儲煤質

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


              //剩余部分添加到緩沖區
              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,將字符串對應的一段數據寫入
            }

            字符串讀(讀取一行字符串)
            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;  //讀出一個字節

               // 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定義了輸入輸出符,可以象基本類型的數據一樣使用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中多數類都從CObject類派生,CObject類與CArchive類有著良好的合作關系,能實現將對象序列化儲存到文件或其他媒介中去,或者讀取預先儲存的對象,動態建立對象等功能。

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

            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; }

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

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

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


            //將對象寫入到緩沖區
            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成員,按其中的代碼寫入類中數據。
              ((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,按其中代碼讀入對象數據。
              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 夢在天涯 閱讀(3114) 評論(1)  編輯 收藏 引用 所屬分類: CPlusPlus

            評論

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

            好東東  回復  更多評論   

            公告

            EMail:itech001#126.com

            導航

            統計

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

            常用鏈接

            隨筆分類

            隨筆檔案

            收藏夾

            Blogs

            c#(csharp)

            C++(cpp)

            Enlish

            Forums(bbs)

            My self

            Often go

            Useful Webs

            Xml/Uml/html

            搜索

            •  

            積分與排名

            • 積分 - 1807503
            • 排名 - 5

            最新評論

            閱讀排行榜

            亚洲а∨天堂久久精品| 久久亚洲精品中文字幕三区| 精品国产一区二区三区久久久狼| 国产精品久久久久9999高清| 久久国产精品波多野结衣AV | 91精品国产色综久久 | 色综合久久夜色精品国产| 久久影院综合精品| 亚洲国产成人精品女人久久久 | 久久久久久久久久久| 久久精品国产99国产精品| 国产91久久精品一区二区| 国产亚洲美女精品久久久| 亚洲日本va中文字幕久久| 久久午夜免费视频| 久久精品国产亚洲综合色| 欧美久久综合性欧美| 久久免费视频1| 久久一本综合| 伊人伊成久久人综合网777| 久久综合伊人77777| 国产精品无码久久综合 | 伊人久久大香线焦综合四虎| 国产精品中文久久久久久久| 无码国内精品久久人妻麻豆按摩| 国产精品99精品久久免费| 久久久久久精品无码人妻| 亚洲v国产v天堂a无码久久| 精品久久久久久无码中文字幕| 国产精品久久久久久搜索| 国产精品国色综合久久| 久久久久高潮毛片免费全部播放 | 久久久久se色偷偷亚洲精品av| 国产精品久久久天天影视香蕉| 狠狠综合久久综合中文88| 精品久久久久久亚洲| 99久久成人国产精品免费| 国产精品久久久久久福利漫画 | 久久精品亚洲中文字幕无码麻豆| 亚洲香蕉网久久综合影视| 亚洲色欲久久久综合网|