• <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>

            一.概述

            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-09-14 15:17 艾凡赫 閱讀(1146) 評論(0)  編輯 收藏 引用 所屬分類: MFC技術C++
            久久久久国产日韩精品网站| 日产精品久久久久久久性色| 午夜人妻久久久久久久久| 国产高清国内精品福利99久久| 久久精品中文字幕无码绿巨人| 中文字幕人妻色偷偷久久| 欧美午夜A∨大片久久 | 激情伊人五月天久久综合| 精品国产乱码久久久久软件| 久久有码中文字幕| 四虎久久影院| 久久天天躁夜夜躁狠狠躁2022| 伊人久久成人成综合网222| 午夜视频久久久久一区 | 一本一道久久综合狠狠老| 婷婷久久五月天| 亚洲AV日韩精品久久久久久| 国产精品免费福利久久| 伊人久久综合热线大杳蕉下载| 精品久久综合1区2区3区激情| 久久天天躁狠狠躁夜夜2020老熟妇| 欧美伊人久久大香线蕉综合69| 久久久久国产精品嫩草影院| 久久精品亚洲精品国产色婷| 91久久精品国产91性色也| 日本欧美国产精品第一页久久| 亚洲色大成网站WWW久久九九| 国产午夜精品久久久久免费视| 日韩欧美亚洲综合久久影院d3| 久久久久婷婷| 国内精品久久久久久99蜜桃| 国产成人精品久久| 伊人久久大香线蕉av不卡| 93精91精品国产综合久久香蕉| 国产精品久久久久免费a∨| 久久免费的精品国产V∧| 久久久久一本毛久久久| 精品久久久噜噜噜久久久| 亚洲乱码日产精品a级毛片久久 | 国产女人aaa级久久久级| 狠狠色丁香久久婷婷综合_中|