VC中对象的序列化与文件I/O
轉(zhuǎn)自:http://huigezrx.blog.163.com/blog/static/32101652200911291140729/
持久性和序列化
持久性是對(duì)象所有的保存和加載其狀態(tài)數(shù)據(jù)的能力。具有這種能力的對(duì)象能夠在應(yīng)用程序結(jié)束之前以某種方式將當(dāng)前的對(duì)象狀態(tài)數(shù)據(jù)記錄下來(lái),當(dāng)程序再次運(yùn)行時(shí),通過(guò)對(duì)這些數(shù)據(jù)的讀取而恢復(fù)到上一次任務(wù)結(jié)束時(shí)的狀態(tài)。由于絕大多數(shù)的MFC類(lèi)是直接或間接由MFC的CObject類(lèi)派生出來(lái)的,因此這些MFC類(lèi)都具有保存和加載對(duì)象狀態(tài)的能力,是具有持久性的。在使用應(yīng)用程序向?qū)晌臋n/視結(jié)構(gòu)的程序框架時(shí),就已經(jīng)為應(yīng)用程序提供了用于對(duì)象狀態(tài)數(shù)據(jù)保存和加載的基本代碼。
為實(shí)現(xiàn)對(duì)象的持久性,通常多以字節(jié)流的形式將記錄對(duì)象狀態(tài)的數(shù)據(jù)存放到磁盤(pán)上,這種將狀態(tài)數(shù)據(jù)保存到磁盤(pán)和從磁盤(pán)恢復(fù)到內(nèi)存的過(guò)程稱(chēng)為序列化。序列化是MFC的一個(gè)重要概念,是MFC文檔/視圖結(jié)構(gòu)應(yīng)用程序能進(jìn)行文檔打開(kāi)、保存等操作的基礎(chǔ)。當(dāng)在MFC框架程序中裝載或保存一個(gè)文件時(shí),除了打開(kāi)文件以供程序讀寫(xiě)外,還傳遞給應(yīng)用程序一個(gè)相關(guān)的CArchive對(duì)象,并以此實(shí)現(xiàn)對(duì)持久性數(shù)據(jù)的序列化。
大多數(shù)MFC應(yīng)用程序在實(shí)現(xiàn)對(duì)象的持久性時(shí)并非直接用MFC的CFile類(lèi)對(duì)磁盤(pán)文件進(jìn)行讀寫(xiě)(有關(guān)CFile類(lèi)的詳細(xì)介紹將在下一節(jié)進(jìn)行),而是通過(guò)使用CArchive對(duì)象并由其對(duì)CFile成員函數(shù)進(jìn)行調(diào)用來(lái)執(zhí)行文件I/O操作。CArchive類(lèi)支持復(fù)合對(duì)象的連續(xù)二進(jìn)制形式的輸入輸出。在構(gòu)造一個(gè)CArchive對(duì)象或?qū)⑵溥B接到一個(gè)表示打開(kāi)的文件的CFile對(duì)象后,可以指定一個(gè)檔案是被裝載還是被保存。MFC允許使用操作符“<<”和“>>”來(lái)對(duì)多種原始數(shù)據(jù)類(lèi)型進(jìn)行序列化。這些原始數(shù)據(jù)類(lèi)型包括BYTE,WORD,LONG,DWORD,float,double,int,unsigned int,short和char等。
對(duì)于其他由MFC類(lèi)來(lái)表示的非原始數(shù)據(jù)類(lèi)型如CString對(duì)象等的序列化則可以通過(guò)對(duì)“<<”和“>>”運(yùn)算符的重載來(lái)解決,可以用此方式進(jìn)行序列化的MFC類(lèi)和結(jié)構(gòu)有CString,CTime,CTimeSpan,COleVariant,COleCurreny,COleDateTime,COleDateTimeSpan,CSize,CPoint,CRect,SIZE,POINT和RECT等。除了操作符“<<”和“>>”之外還可以調(diào)用CArchive類(lèi)成員函數(shù)Read()和Write()來(lái)完成序列化。下面這段代碼展示了通過(guò)操作符對(duì)int型變量VarA、VarB的序列化過(guò)程:
?
| ? // 將VarA、VarB存儲(chǔ)到檔案中 CArchive ar (&file, CArchive::store); ar << VarA << VarB; …… // 從檔案裝載VarA、VarB CArchive ar (&file, CArchive::load) ar >> VarA >> VarB; |
?
?
CArchive類(lèi)僅包含有一個(gè)數(shù)據(jù)成員m__pDocument。在執(zhí)行菜單上的打開(kāi)或保存命令時(shí),程序框架將會(huì)把該數(shù)據(jù)成員設(shè)置為要被序列化的文檔。另外需要特別注意的是:在使用CArchive類(lèi)時(shí),要保證對(duì)CArchive對(duì)象的操作與文件訪(fǎng)問(wèn)權(quán)限的統(tǒng)一。
在本文下面將要給出的示例程序中,將對(duì)繪制連線(xiàn)所需要的關(guān)鍵點(diǎn)坐標(biāo)和坐標(biāo)個(gè)數(shù)等持久性對(duì)象進(jìn)行序列化。其中文檔類(lèi)的成員變量m_nCount和m_ptPosition[100]分別記錄了當(dāng)前點(diǎn)的個(gè)數(shù)和坐標(biāo),初始值為0。當(dāng)鼠標(biāo)點(diǎn)擊客戶(hù)區(qū)時(shí)將對(duì)點(diǎn)的個(gè)數(shù)進(jìn)行累加,并保存當(dāng)前點(diǎn)的坐標(biāo)位置。隨后通過(guò)Invalidate()函數(shù)發(fā)出WM_PAINT消息通知窗口對(duì)客戶(hù)區(qū)進(jìn)行重繪,在重繪代碼中對(duì)這些點(diǎn)擊過(guò)的點(diǎn)進(jìn)行繪圖連線(xiàn):
?
| ? void CSample04View::OnLButtonDown(UINT nFlags, CPoint point) { // 獲取指向文檔類(lèi)的指針 CSample04Doc* pDoc = GetDocument(); // 保存當(dāng)前鼠標(biāo)點(diǎn)擊位置 pDoc->m_ptPosition[pDoc->m_nCount] = point; if (pDoc->m_nCount < 100) pDoc->m_nCount++; // 刷新屏幕 Invalidate(); CView::OnLButtonDown(nFlags, point); } …… void CSample04View::OnDraw(CDC* pDC) { CSample04Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // 對(duì)點(diǎn)擊的點(diǎn)進(jìn)行連線(xiàn)繪圖 pDC->MoveTo(pDoc->m_ptPosition[0]); for (int i = 1; i < pDoc->m_nCount; i++) pDC->LineTo(pDoc->m_ptPosition[i]); } |
?
?
從上述程序代碼不難看出,為了能保存繪制結(jié)果需要對(duì)文檔類(lèi)的成員變量m_nCount和m_ptPosition[100]進(jìn)行序列化處理。而文檔類(lèi)成員函數(shù)Serialize()則通過(guò)Archive類(lèi)為這些持久性對(duì)象的序列化提供了功能上的支持。下面的代碼完成了對(duì)持久性對(duì)象的保存和加載:
?
| ? if (ar.IsStoring()) { // 存儲(chǔ)持久性對(duì)象到檔案 ar << m_nCount; for (int i = 0; i < m_nCount; i++) ar << m_ptPosition[i]; } else { // 從檔案裝載持久性對(duì)象 ar >> m_nCount; for (int i = 0; i < m_nCount; i++) ar >> m_ptPosition[i]; } |
?
自定義持久類(lèi)
為了使一個(gè)類(lèi)的對(duì)象成為持久的,可以自定義一個(gè)持久類(lèi),將持久性數(shù)據(jù)的存儲(chǔ)和加載的工作交由自定義類(lèi)自己去完成。這種處理方式也更加符合面向?qū)ο蟮某绦蛟O(shè)計(jì)要求。可以通過(guò)下面幾個(gè)基本步驟來(lái)創(chuàng)建一個(gè)能序列化其成員變量的自定義持久類(lèi):
1. 直接或間接從CObject類(lèi)派生出一個(gè)新類(lèi)。
2. 在類(lèi)的聲明部分包含MFC的DECLARE_SERIAL宏,該宏只需要將類(lèi)名作為參數(shù)。
3. 重載基類(lèi)的Serialize()函數(shù),并添加對(duì)數(shù)據(jù)成員進(jìn)行序列化的代碼。
4. 如果構(gòu)造函數(shù)沒(méi)有一個(gè)空的缺省的構(gòu)造函數(shù)(不含任何參數(shù)),為其添加一個(gè)。
5. 在類(lèi)的實(shí)現(xiàn)部分,添加MFC的IMPLEMENT_SERIAL宏。該宏需要三個(gè)參數(shù):類(lèi)名,基類(lèi)名和一個(gè)方案號(hào)。其中方案號(hào)是一個(gè)相當(dāng)于版本號(hào)的整數(shù),每當(dāng)改變了類(lèi)的序列化數(shù)據(jù)格式后就應(yīng)當(dāng)及時(shí)更改此數(shù)值。
根據(jù)上述步驟不難對(duì)上一小節(jié)中的序列化代碼進(jìn)行封裝,封裝后的持久類(lèi)CPosition負(fù)責(zé)對(duì)類(lèi)成員變量m_nCount和m_ptPosition[100]的序列化,封裝后的代碼如下:
?
| ? // CPosition類(lèi)聲明部分: class CPosition : public CObject { DECLARE_SERIAL(CPosition) CPosition(); int m_nCount; CPoint m_ptPosition[100]; void Serialize(CArchive& ar); CPoint GetValue(int index); void SetValue(int index, CPoint point); virtual ~CPosition(); }; …… // CPosition類(lèi)實(shí)現(xiàn)部分: IMPLEMENT_SERIAL(CPosition, CObject, 0) CPosition::CPosition() { // 對(duì)類(lèi)成員進(jìn)行初始化 m_nCount = 0; for (int i = 0; i < 100; i++) m_ptPosition[i] = CPoint (0, 0); } CPosition::~CPosition() { } void CPosition::SetValue(int index, CPoint point) { // 設(shè)置指定點(diǎn)的坐標(biāo)值 m_ptPosition[index] = point; } CPoint CPosition::GetValue(int index) { // 獲取指定點(diǎn)的坐標(biāo)值 return m_ptPosition[index]; } void CPosition::Serialize(CArchive &ar) { CObject::Serialize(ar); if (ar.IsStoring()) { // 存儲(chǔ)持久性對(duì)象到檔案 ar << m_nCount; for (int i = 0; i < m_nCount; i++) ar << m_ptPosition[i]; } else { // 從檔案裝載持久性對(duì)象 ar >> m_nCount; for (int i = 0; i < m_nCount; i++) ar >> m_ptPosition[i]; } } |
?
?
在創(chuàng)建了自定義持久類(lèi)CPosition后,可以通過(guò)該類(lèi)對(duì)鼠標(biāo)點(diǎn)擊過(guò)的點(diǎn)的坐標(biāo)進(jìn)行管理,由于序列化的工作已由類(lèi)本身完成,因此只需在文檔類(lèi)的Serialize()函數(shù)中對(duì)CPosition的Serialize()成員函數(shù)進(jìn)行調(diào)用即可:
?
| ? void CSample04Doc::Serialize(CArchive& ar) { // 使用定制持久類(lèi) m_Position.Serialize(ar); if (ar.IsStoring()) { } else { } } |
?
文件I/O
雖然使用CArchive類(lèi)內(nèi)建的序列化功能是保存和加載持久性數(shù)據(jù)的便捷方式,但有時(shí)在程序中需要對(duì)文件處理過(guò)程擁有更多的控制權(quán),對(duì)于這種文件輸入輸出(I/O)服務(wù)的需求,Windows提供了一系列相關(guān)的API函數(shù),并由MFC將其封裝為CFile類(lèi),提供了對(duì)文件進(jìn)行打開(kāi),關(guān)閉,讀,寫(xiě),刪除,重命名以及獲取文件信息等文件操作的基本功能,足以處理任意類(lèi)型的文件操作。CFile類(lèi)是MFC文件類(lèi)的基類(lèi),支持無(wú)緩沖的二進(jìn)制輸入輸出,也可以通過(guò)與CArchive類(lèi)的配合使用而支持對(duì)MFC對(duì)象的帶緩沖的序列化。
CFile類(lèi)包含有一個(gè)公有型數(shù)據(jù)成員m_hFile,該數(shù)據(jù)成員包含了同CFile類(lèi)對(duì)象相關(guān)聯(lián)的文件句柄。如果沒(méi)有指定句柄,則該值為CFile::hFileNull。由于該數(shù)據(jù)成員所包含的意義取決于派生的類(lèi),因此一般并不建議使用m_hFile。
通過(guò)CFile類(lèi)來(lái)打開(kāi)文件可以采取兩種方式:一種方式是先構(gòu)造一個(gè)CFile類(lèi)對(duì)象然后再調(diào)用成員函數(shù)Open()打開(kāi)文件,另一種方式則直接使用CFile類(lèi)的構(gòu)造函數(shù)去打開(kāi)一個(gè)文件。下面的語(yǔ)句分別演示了用這兩種方法打開(kāi)磁盤(pán)文件“C:\TestFile.txt”的過(guò)程:
?
| ? // 先構(gòu)造一個(gè)實(shí)例,然后再打開(kāi)文件 CFile file; file.Open(“C:\\TestFile.txt”, CFile::modeReadWrite); …… // 直接通過(guò)構(gòu)造函數(shù)打開(kāi)文件 CFile file(“C:\\TestFile.txt”, CFile::modeReadWrite); |
?
?
其中參數(shù)CFile::modeReadWrite是打開(kāi)文件的模式標(biāo)志,CFile類(lèi)中與之類(lèi)似的標(biāo)志還有十幾個(gè),現(xiàn)集中列表如下:
?
| ? 文件模式標(biāo)志 | ? 說(shuō)明 |
| ? CFile::modeCreate | ? 創(chuàng)建方式打開(kāi)文件,如文件已存在則將其長(zhǎng)度設(shè)置為0 |
| ? CFile::modeNoInherit | ? 不允許繼承 |
| ? CFile::modeNoTruncate | ? 創(chuàng)建文件時(shí)如文件已存在不對(duì)其進(jìn)行截?cái)?/p> |
| ? CFile::modeRead | ? 只讀方式打開(kāi)文件 |
| ? CFile::modeReadWrite | ? 讀寫(xiě)方式打開(kāi)文件 |
| ? CFile::modeWrite | ? 寫(xiě)入方式打開(kāi)文件 |
| ? CFile::shareCompat | ? 在使用過(guò)程中允許其他進(jìn)程同時(shí)打開(kāi)文件 |
| ? CFile::shareDenyNone | ? 在使用過(guò)程中允許其他進(jìn)程對(duì)文件進(jìn)行讀寫(xiě) |
| ? CFile::shareDenyRead | ? 在使用過(guò)程中不允許其他進(jìn)程對(duì)文件進(jìn)行讀取 |
| ? CFile::shareDenyWrite | ? 在使用過(guò)程中不允許其他進(jìn)程對(duì)文件進(jìn)行寫(xiě)入 |
| ? CFile::shareExclusive | ? 取消對(duì)其他進(jìn)程的所有訪(fǎng)問(wèn) |
| ? CFile::typeBinary | ? 設(shè)置文件為二進(jìn)制模式 |
| ? CFile::typeText | ? 設(shè)置文件為文本模式 |
?
?
這些標(biāo)志可以通過(guò)“或”運(yùn)算符而同時(shí)使用多個(gè),并以此來(lái)滿(mǎn)足多種需求。例如,需要以讀寫(xiě)方式打開(kāi)文件,如果文件不存在就創(chuàng)建一個(gè)新的,如果文件已經(jīng)存在則不將其文件長(zhǎng)度截?cái)酁?。為滿(mǎn)足此條件,可用CFile::modeCreate、CFile::modeReadWrite和CFile::modeNoTruncate等幾種文件模式標(biāo)志來(lái)打開(kāi)文件:
CFile file ("C:\\TestFile.txt", CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate);
在打開(kāi)的文件不再使用時(shí)需要將其關(guān)閉,即可以用成員函數(shù)Close()關(guān)閉也可以通過(guò)CFile類(lèi)的析構(gòu)函數(shù)來(lái)完成。當(dāng)采取后一種方式時(shí),如果文件還沒(méi)有被關(guān)閉,析構(gòu)函數(shù)將負(fù)責(zé)隱式調(diào)用Close()函數(shù)去關(guān)閉文件,這也表明創(chuàng)建在堆上的CFile類(lèi)對(duì)象在超出范圍后將自動(dòng)被關(guān)閉。由于調(diào)用了對(duì)象的析構(gòu)函數(shù),因此在文件被關(guān)閉的同時(shí)CFile對(duì)象也被銷(xiāo)毀,而采取Close()方式關(guān)閉文件后,CFile對(duì)象仍然存在。所以,在顯式調(diào)用Close()函數(shù)關(guān)閉一個(gè)文件后可以繼續(xù)用同一個(gè)CFile對(duì)象去打開(kāi)其他的文件。
文件讀寫(xiě)是最常用的文件操作方式,主要由CFile類(lèi)成員函數(shù)Read()、Write()來(lái)實(shí)現(xiàn)。其函數(shù)原型分別為:
?
| ? UINT Read( void* lpBuf, UINT nCount ); void Write( const void* lpBuf, UINT nCount ); |
?
?
參數(shù)lpBuf為指向存放數(shù)據(jù)的緩存的指針,nCount為要讀入或?qū)懭氲淖止?jié)數(shù),Read()返回的為實(shí)際讀取的字節(jié)數(shù),該數(shù)值小于或等于nCount,如果小于nCount則說(shuō)明已經(jīng)讀到文件末尾,可以結(jié)束文件讀取,如繼續(xù)讀取,將返回0。因此通常可以將實(shí)際讀取字節(jié)數(shù)是否小于指定讀取的字節(jié)數(shù)或是否為0作為判斷文件讀取是否到達(dá)結(jié)尾的依據(jù)。下面這段代碼演示了對(duì)文件進(jìn)行一次性寫(xiě)入和循環(huán)多次讀取的處理過(guò)程:
?
| ? // 創(chuàng)建、寫(xiě)入方式打開(kāi)文件 CFile file; file.Open("C:\\TestFile.txt", CFile::modeWrite | CFile::modeCreate); // 寫(xiě)入文件 memset(WriteBuf, 'a', sizeof(WriteBuf)); file.Write(WriteBuf, sizeof(WriteBuf)); // 關(guān)閉文件 file.Close(); // 只讀方式打開(kāi)文件 file.Open("C:\\TestFile.txt", CFile::modeRead); while (true) { // 讀取文件數(shù)據(jù) int ret = file.Read(ReadBuf, 100); …… // 如果到達(dá)文件結(jié)尾則中止循環(huán) if (ret < 100) break; } // 關(guān)閉文件 file.Close(); |
?
?
Write()和Read()函數(shù)執(zhí)行完后將自動(dòng)移動(dòng)文件指針,因此不必再顯示調(diào)用Seek()函數(shù)去定位文件指針。包含有文件定位函數(shù)的完整代碼如下所示:
?
| ? // 創(chuàng)建、寫(xiě)入方式打開(kāi)文件 CFile file; file.Open("C:\\TestFile.txt", CFile::modeWrite | CFile::modeCreate); // 寫(xiě)入文件 memset(WriteBuf, 'a', sizeof(WriteBuf)); file.SeekToBegin(); file.Write(WriteBuf, sizeof(WriteBuf)); // 關(guān)閉文件 file.Close(); // 只讀方式打開(kāi)文件 file.Open("C:\\TestFile.txt", CFile::modeRead); while (true) { // 文件指針 static int position = 0; // 移動(dòng)文件指針 file.Seek(position, CFile::begin); // 讀取文件數(shù)據(jù) int ret = file.Read(ReadBuf, 100); position += ret; …… // 如果到達(dá)文件結(jié)尾則中止循環(huán) if (ret < 100) break; } // 關(guān)閉文件 file.Close(); |
?
?
小結(jié)
轉(zhuǎn)載于:https://www.cnblogs.com/phoenixzq/archive/2011/12/01/2270412.html
總結(jié)
以上是生活随笔為你收集整理的VC中对象的序列化与文件I/O的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: (转)一台服务器安装两个tomcat6
- 下一篇: web访问负载均衡的实现