VC++技術內幕(第四版)筆記(第16章)

/********************************************/

第十六章:讀和寫文檔--SDI應用程序


一,序列化:

1,序列化:對象可以被持續,即當程序退出時候它們被存盤,當程序重啓時候它們又可被恢復,對象這種存盤和恢復處理過程稱之爲序列化。
注意:MFC庫中,不能利用序列化來代替數據庫管理系統。與文檔相關的所有對象只能在某個單獨的磁盤文件中進行順序讀寫,而不支持對象在磁盤文件中隨機讀寫。

2,磁盤文件和歸檔(Archives):
1)在MFC庫中,磁盤文件是通過CFile類的對象來表示的。
2)如果應用程序不直接利用磁盤I/O,而只依賴於序列化處理過程,則可以避免直接使用CFile對象。
3)在serialize函數(序列化函數)與CFile對象之間,還有一個歸檔對象(CArchive類對象),歸檔對象爲CFile對象緩存數據,同時還保存一個內部標記,用來標示歸檔存檔(寫盤)還是載入(讀盤)。
4)每次只能有一個活動的歸檔與文件相連。應用程序框架會很好管理CFile對象及CArchive對象的創建,爲CFile對象打開相應的磁盤文件,並且將相應的歸檔對象與文件對象相連。
5)關係圖:
持續文檔對象 <--> Serialize <--> CArchive對象 <--> CFile對象 <--> 磁盤
6)當用戶選擇了File Open或File Save命令時,應用程序框架自動調用Serialize函數。

3,使類可序列化:
1)可序列化的類必須直接或間接從CObject派生,並且在類聲明中,必須包括DECLARE_SERIAL宏調用,在類的實現文件中必須包括IMPLEMENT_SERIAL宏調用。
2)編寫Serialize函數。
如:
// example for CObject::Serialize
void CAge::Serialize( CArchive& ar )
 {
 CObject::Serialize( ar );
     if( ar.IsStoring() )//IsStoring來判斷當前歸檔是被用來存入還是被用來載入的。
     ar << m_years;
     else
     ar >> m_years;
 }
注意一:插入運算符對值重載,析取運算符對應用重載,有時必須強制轉換才能適應編譯器:
如:m_nType是枚舉類型,則:ar>>(int&)m_nType;ar<<(int)m_nType;
注意二:插入和析取運算符並不適用於CObject派生類內嵌對象。
3)大多數序列化函數都需要調用基類的Serialize函數(一般在第一行調用,如CStudent是從CPerson派生,那麼CStudent中Serialize函數第一行就應該爲CPerson::Serialize(ar);)。CObject類Serialize是虛函數並且沒做任何工作,因而2)中類子中沒調用CObject::Serialize函數。
4)對於CObject派生類的內嵌對象中的Serialize函數中總是需要直接調用內嵌對象的Serialize函數。

4,使集合序列化:
1)所有的集合類都是從CObject類派生,並且在集合類聲明中都包含有DECLARE_SERIAL宏調用,應此可以調用集合類的Serialize成員函數,方便使集合序列化。
2)如調用了由一組對象組成的COblist集合的Serialize函數,則COblist集合中每個對象的Serialize函數會被依次調用。
3)如果集合中包含了指向不同類對象的指針,則所有的類名都被相應地存儲到歸檔中,以便所有的對象在被創建時都能夠調用相應類的構造函數。
4)如果包容對象(如文檔)中包含了一個內嵌集合,則被裝入的數據會被追加進現存集合中。(因此有必要在載入前對現存的集合清空。通常由DeleteContents函數完成。)
5)當CObject指針集合被從歸檔中載入時,集合中的每個對象會被按如下步驟處理:
×指定對象的類。
×爲每一個對象分配堆存儲空間。
×將對象數據載入到新申請的內存中。
×將新對象的指針存進集合中。
6)當用戶選擇了File菜單的Save或Open菜單項,應用程序框架隨即會創建CArchive對象(以及內部的CFile對象),然後再調用文檔類的Serialize函數,並且將CArchive對象的引用傳遞給它。然後派生文檔類的Serialize函數會對每一個非臨時數據成員進行序列化。


二,SDI應用程序:

1,SDI MFC應用程序的具體啓動步驟:
1)Windows將程序裝入內存。
2)構造全局對象theApp。(當程序被載入時候,所有的全局對象都會立刻被創建。)
3)Windows調用全局函數WinMain。
4)WinMain自動搜索CWinApp派生類的唯一實例。
5)WinMain調用theApp的InitInstance函數,該函數在派生應用程序中被重載。
6)被重載的InitInstance函數啓動文檔的載入以及主框架和視圖窗口的顯示處理過程。
7)WinMain調用theApp的Run成員函數,啓動窗口消息和命令消息的分發處理過程。
還可以對其它的CWinApp成員函數進行重載。當應用程序被中止,且所有窗口被關閉後,ExitInstance函數被調用。

2,派生的應用程序類InitInstance函數中可以看到這樣代碼:
CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CSingleDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CMy15aDoc),
  RUNTIME_CLASS(CMainFrame),       // main SDI frame window
  RUNTIME_CLASS(CMy15aView));
 AddDocTemplate(pDocTemplate);
通過這組代碼將程序類,文檔類,視圖窗口類以及主框架類建立類之間的相互關係(注意這裏是類之間相互關係)。
(即通過AddDocTemplate調用將所有的應用程序元素聯繫在一起)
說明:
1)應用程序對象在模板被創建之前已經存在,但此時文檔,視圖,以及框架對象還沒有被創建。應用程序框架在以後需要的時候會通過動態創建這些對象,這裏是通過適用RUNTIME_CLASS來實現的。
2)P338頁兩個圖:類之間關係圖 和 對象之間關係圖 這裏掠過,找電子版看看。
3)IDR_MAINFRAME 是用來標示字符串表資源的。IDR_MAINFRAME 所標示的字符串被分成一些以"/n"結尾的子字符串。當應用程序執行時候,這些子字符串會在各種地方出現。
在RC文件StringTable中可以找到IDR_MAINFRAME串,串的具體對應關係參看P339頁。

3,SDI文檔多視圖兩種技術:提供菜單項供用戶選擇視圖,將多視圖安排在切分窗口中。

4,創建空文檔……CWinApp::OnFileNew函數
應用程序類的在調用了AddDocTemplate函數之後,通過CWinApp::ProcessShellCommand函數間接調用了CWinApp成員函數OnFileNew完成以下工作:
1)構造文檔對象,但並不從磁盤中讀取數據。
2)構造主框架對象,並創建主框架窗口,但並不對其顯示。主框架窗口包括IDR_MAINFRAME菜單,工具欄和狀態欄。
3)構造視圖對象,並創建視圖窗口,但並不對它進行顯示。
4)建立文檔,主框架和視圖對象之間的相互聯繫(注意這裏是對象之間聯繫)
5)調用文檔對象的OnNewDocument虛成員函數。它會調用DeleteContents虛函數。
6)調用視圖對象的CView::OnInitiaUpdate虛成員函數。
7)調用框架對象的CFrameWnd::ActivateFrame虛成員函數,以便顯示出具有菜單,視圖窗口和控制欄的主框架窗口。
說明:上面一些函數是通過框架間接調用。

在SDI應用程序中,文檔,主框架以及視圖對象都僅被創建一次,並且將存在於程序的整個運行過程中。
CWinApp::OnFileNew函數被InitInstance函數所調用,當用戶選擇了File New 菜單項的時候也調用(此時不需要再構造文檔框架視圖對象,而是利用現存的完成上述後三個步驟)。
注意:OnFileNew函數總是要調用DeleteContents函數,以便將文檔清空。

5,CDocument::OnNewDocument
如果SDI應用程序不重新使用相同的文檔對象,則也就沒必要使用OnNewDocument函數(因爲可以在文檔類構造函數中完成所有的文檔初始化工作)。但一般還是得(必須)對OnNewDocument函數進行重載,以便每次用戶選擇File New或File Open時候,都能利用它來初始化文檔對象。
說明:
一般,在構造函數裏儘量少的做一些工作,構造函數做的工作越少,失敗的機會就越小。象CDocument::OnNewDocument和CView::OnInitialUpdate這樣的函數是完成初始化工作的好地方。

6,連接File Open與系列化代碼………OnFileOpen函數
當AppWizard創建應用程序時,它會自動將File Open菜單項映射到CWinApp的OnFileOpen成員函數。
OnFileOpen函數進一步調用一組函數來完成一下工作:
1)提示用戶選擇一個文件。
2)調用已經存在文檔對象的CDocument::OnOpenDocument虛成員函數。該函數將打開文件,調用DeleteContents函數,再創建一個用於裝入數據的CArchive對象,然後調用文檔的Serialize函數從歸檔中載入數據。
3)調用視圖的OnInitialUpdate函數。

除了使用File Open菜單項外,還可以選擇使用最近使用過的文件列表。(框架會記錄4個最近使用過的文件,並將它們的名字顯示在File菜單中。這些文件名在程序的運行過程中被記錄在Windows的註冊表中。
說明:
可以改變最近使用文件數。(在InitInstance函數裏爲LoadStdProfileSetting函數提供適當參數)
CWinApp::LoadStdProfileSettings
void LoadStdProfileSettings( UINT nMaxMRU = _AFX_MRU_COUNT );
//nMaxMRU:The number of recently used files to track.
//Call this member function from within the InitInstance member function to enable and load the list of most recently used (MRU) files and last preview state. If nMaxMRU is 0, no MRU list will be maintained.

7,CDocument::DeleteContents
1)當File New或File Open菜單項被選中CDocument::OnNewDocument或CDocument::OnOpenDocument函數都要調用CDocument::DeleteContents函數(清除現存文檔對象內容)。
即:當文檔對象第一次被創建之後,CDocument::DeleteContents 會被立刻調用,而當文檔被關閉,它再次被調用。
2)常在派生類中重載DeleteContents 函數完成對文檔對象中內容清除工作。
3) CDocument::DeleteContents      Called by the framework to delete the document’s data without destroying the CDocument object itself. It is called just before the document is to be destroyed. It is also called to ensure that a document is empty before it is reused. This is particularly important for an SDI application, which uses only one document; the document is reused whenever the user creates or opens another document. Call this function to implement an “Edit Clear All” or similar command that deletes all of the document’s data. The default implementation of this function does nothing. Override this function to delete the data in your document.

8,將File Save 和File Save As與系列化代碼相連接:
1)當AppWizard在創建應用程序時,將File Save菜單項映射到CDocument類的OnFileSave成員函數。
2)OnFileSave函數調用CDocument::OnSaveDocument函數,OnSaveDocument函數使用歸檔對象(存入)來調用文檔的Sarialize函數。
3)File Save As菜單象以類似方式處理。映射CDocument::OnFileSaveAs函數,OnFileSaveAs函數會調用OnSaveDocument函數。
4)文檔存盤的必要的文件管理工作都是由應用程序框架來完成。
注意:
File New和File Open被映射到應用程序類成員函數,而File Save和File Save As則被映射到文檔類成員函數。

9,文檔的‘脹’標誌
許多面向文檔的應用程序跟蹤用戶對文檔的修改,當用戶試圖關閉文檔或退出時候,應用程序彈出對話框詢問是否需要將文檔保存。
1)MFC通過CDocument::m_bModified來支持這種功能。文檔被修改(變脹了)則m_bModified=TRUE,否則爲FALSE。
2)m_bModified爲protected類型,可以通過CDocument類中的SetModifiedFlag和IsModified成員函數來訪問。(注意:在MSDN中不能查出CDocument類m_bModified數據成員,不過追蹤CDocument類定義可以發現如下定義:protected: BOOL m_bModified; )
3)CDocument::SetModifiedFlag
void SetModifiedFlag( BOOL bModified = TRUE );

CDocument::IsModified
BOOL IsModified( );

10,程序註冊
1)利用AppWizard在程序創建Step-4使用Advanced選項爲程序添加文件擴展名(或在StringTable裏IDR_MAINFRAME裏添加)
2)在InitInstance函數里加入下列代碼行:RegisterShellFileTypes(TRUE);(注意添加的位置在AddDocTemplate函數之後!)

11,允許程序的拖放:
如果希望運行中的程序可以打開拖到其上的文件,則必須在主程序框架窗口中調用CWnd的DragAcceptFiles函數。
在InitInstance函數中(函數未)添加下列代碼:m_pMainWnd->DragAcceptFiles();

//////////////////////////////////////////////////
/////////////////////////////////////////////////
///////////////////////////////////////////////

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章