⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap7_2.htm

📁 非常好的应用vc++6.0进行相关网络程序开发的教程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
    <p>下面是使用数组模板类的例子:</p>
    <p>CArray&lt;CMyClass,CMyClass&amp;&gt; myArray;</p>
    <p>CMyClass myClass;</p>
    <p>myArray-&gt;Add(myClass);</p>
    <p ALIGN="JUSTIFY">映射类以一种字典的方式组织数据。每个元素由一个关键字和一个数值项组成,关键字用作数值项的标识符,在集合中不允许重复,必须是唯一的。如果给出一个关键字,映射类会很快找到对应的数值项。映射查找是以哈希表的方式进行的,因此在映射中查找数值项的速度很快。除了映射类模板外,预定义的映射类能支持CString对象、字、CObject指针和无类型指针。比如,CMapWordToOb类创建一个映射表对象后,就可以用WORD类型的变量作为关键字来寻找对应的CObject指针。映射类最适用于需要根据关键字进行快速检索的场合。</p>
    <p>要访问映射中的数据,可以用GetStartPosition()定位到开始处,再用GetNextAssoc访问映射表中的成员。</p>
    <p>要删除映射中的数据,可以用GetStartPosition和GetNextAssoc遍历并用delete删除对象,然后调用RemoveAll。</p>
    <p>下面是使用CMap模板类的例子:</p>
    <p>CMap&lt;CString,LPCSTR,CPerson,CPerson&amp;&gt; myMap;</p>
    <p>CPerson person;</p>
    <p>LPCSTR lpstrName=“Tom”;</p>
    <p>myMap-&gt;SetAt(lpstrName,person);</p>
    <p>有关集合类的使用可以参见MFC的例子COLLECT。</p>
    <p>对于文本编辑器,由于需要动态增加和删除每一行字符串,因此使用CStringList来保存文本编辑器的数据,CStringList中的每一个元素是CString类型的,它代表一行字符。可以把CString看作一个字符数组,但它提供了丰富的成员函数,比字符数组功能强大的多。</p>
    <p>另外,还需要增加一个数据成员nLineNum,用于指示当前编辑行行号。如清单7.3,在文档类的头文件EditorDoc.h中,加入以下代码:</p>
    <p>清单7.3 CEditorDoc.h</p>
    <p>class CEditorDoc : public CDocument</p>
    <p>{</p>
    <p>protected: // create from serialization only</p>
    <p>CEditorDoc();</p>
    <p>DECLARE_DYNCREATE(CEditorDoc)</p>
    <p>// Attributes</p>
    <p>public:</p>
    <p> </p>
    <p>CStringList lines;</p>
    <p>int nLineNum;</p>
    <p>...</p>
    <p>};</p>
    <p>在定义了文档数据成员后,还要对文档数据成员进行初始化。</p>
    <p><font color="#3973DE">初始化文档类的数据成员</font></p>
    <p>当用户启动应用程序,或从应用程序的File菜单种选择New选项时,都需要对文档类的数据成员进行初始化。一般的,类的数据成员的初始化都是在构造函数中完成的,在构造函数调用结束时对象才真正存在。但对于文档来说却不同,文档类的数据成员初始化工作是在OnNewDocument成员函数中完成的,此时文档对象已经存在。为什么呢?这是因为:在单文档界面(SDI)应用程序中,在应用程序启动时,文档对象就已经被创建。文档对象直到主框架窗口被关闭时才被销毁。在用户选择File-New菜单时,应用程序对象并不是销毁原来的文档对象然后重建新的文档对象,而只是重新初始化(Re-Initialization)文档对象的数据成员,这个初始化工作就是应用程序对象的OnFileNew()消息处理成员函数通过调用OnNewDocument()函数来完成的。试想,如果把初始化数据成员的工作放在构造函数中的话,由于对象已经存在,构造函数就无法被调用,也就无法完成初始化数据成员的工作。为了避免代码的重复,在应用程序启动时,应用程序对象也是通过调用OnNewDocument成员函数来初始化文档对象的数据成员的。如果是多文档界面(MDI)程序,则数据成员的初始化也可以放到构造函数中完成。因为在MDI中,选择File-&gt;New菜单时,应用程序对象就让文档模板创建一个新文档并创建对应的框架窗口和视图。但是,为了保证应用程序在单文档和多文档界面之间的可移植性,我们还是建议将文档数据成员的初始化工作放在OnNewDocument()中完成,因为在MDI的应用程序对象的OnFileNew成员函数中,同样会调用文档对象的OnNewDocument成员函数。</p>
    <p>在OnNewDocument成员函数中手工加入代码,如清单7.4。</p>
    <p>清单7.4 OnNewDocument成员函数</p>
    <p>BOOL CEditorDoc::OnNewDocument()</p>
    <p>{</p>
    <p>if (!CDocument::OnNewDocument())</p>
    <p>return FALSE;</p>
    <p>// TODO: add reinitialization code here</p>
    <p>// (SDI documents will reuse this document)</p>
    <p> </p>
    <p>nLineNum=0;</p>
    <p>POSITION pos;</p>
    <p>pos=lines.GetHeadPosition();</p>
    <p>while(pos!=NULL)</p>
    <p>{</p>
    <p>((CString)lines.GetNext(pos)).Empty();</p>
    <p>}</p>
    <p>lines.RemoveAll();</p>
    <p>return TRUE;</p>
    <p>}</p>
    <p>其中pos类型为POSITION,相当于链表的指针,指向链表当前元素。CStringList的成员函数GetHeadPosition()返回链表头指针。链表的GetNext()函数以当前指针为参数,返回下一个元素指针,同时修改pos,使它指向下一个元素。使用强制类型转换将GetNext()函数返回的元素指针转化为CString类型,然后调用CString::Empty()方法清除该行中的所有字符。通过一个while循环,清除所有文本行的数据。最后调用CStringList的RemoveAll()成员函数,清除链表中的所有指针(注意:此时这些指针指向的元素已经被清除)。</p>
    <div align="center"><center><table border="2" cellpadding="2" cellspacing="0" width="100%" bgcolor="#95DDFF">
      <tr>
        <td width="100%">提示:应用程序对象的成员函数CWinApp::OnFileNew()在选择File菜单的New命令时被调用,缺省时在InitInstance()中也会被调用。原理是在InitInstance()中有一个命令行参数的执行过程,当命令行上没有参数时,函数ParseCommandLine(cmdInfo)会调用CCommandLineInfo 
        :: 把m_nShellCommand成员置为CCommandLineInfo::FileNew,这导致ProcessShellCommand成员函数调用OnFileNew。用户可在InitInstance()中显式的调用OnFileNew()。</font></td>
      </tr>
    </table>
    </center></div><font SIZE="3"><p ALIGN="JUSTIFY">应用程序对象的OnFileNew消息处理流程如下:首先判断应用程序是否有多个文档模板,若是,则显示一个对话框让用户选择创建哪种类型的文档(模板)。对话框中显示的字符串是与文档模板对象的构造函数的第一个参数相对应的字符串(若资源中无相应字符串则不显示)。然后该函数调用CDocManager::OpenDocumentFile(NULL)成员函数,打开一个新文件。CDocManager::OpenDocumentFile函数调用了CSingleDocTemplate的OpenDocumentFile,后者完成实际的创建文档、框架、视图工作。文档模板的OpenDocumentFile首先判断文档是否已经被创建,若未创建,则创建一个新文档。然后根据文件名参数是否为空,分别调用CDocument的OnNewDocument( 
    )和CDocument的OnOpenDocument()函数。CDocument的OnNewDocument首先调用DeleteContents(),并将文档修改标志该为FALSE(关闭窗口时将根据文档修改标志决定是否提示用户保存文档)。</p>
    <p> </p>
    <p><font color="#3973DE">清理文档类的数据成员</font></p>
    <p>在关闭应用程序删除文档对象时,或用File-&gt;Open菜单打开一个文档时,需要清理文档中的数据。同文档的初始化一样,文档的清理也不是在文档的析构函数中完成,而是在文档的CDocument::DeleteContents()成员函数中完成的(想想为什么?)。析构函数只用于清除那些在对象生存期都将存在的数据项。DeleteContents()成员函数的调用有两个作用:</p>
    <blockquote>
      <p>1.删除文档的数据;</p>
      <p>2确信一个文档在使用前为空。</p>
    </blockquote>
    <p>前面已经说到,OnNewDocument函数会调用DeleteContents()函数。在用户选择File-&gt;Open菜单时,应用程序对象调用应用程序类的OnFileOpen成员函数,CWinApp::OnFileOpen调用内部的文档管理类CDocManager::OnFileOpen()成员函数,提示用户输入文件名。然后调用CWinApp::OpenDocumentFile打开一个文件。OpenDocumentFile在打开文件后首先调用DeleteContents成员函数清理文档中的数据,确保消除以前打开的文档的数据被清理掉。</p>
    <p>缺省的DeleteContents函数什么也不做。你需要重载DeleteContents函数,并编写自己的文档清理代码。要重载DeleteContents成员函数:</p>
    <p ALIGN="JUSTIFY">从View菜单下选择ClassWizard,启动ClassWizard,选择Message 
    Maps页。在ClassName下拉列表框中选择CEditorDoc,从ObjectIDs列表框选择CEditorDoc,在Message列表框双击DeleteContents。此时DeleteContents出现在Member 
    functions列表框中,并被选中。点Edit Code按钮,开始编辑DeleteContents函数定义。在DeleteContents函数体中加入代码后,如清单7.5所示:</p>
    <p ALIGN="JUSTIFY"> </p>
    <p ALIGN="JUSTIFY">清单7.5 CEditorDoc的DeleteContents成员函数</p>
    <p>void CEditorDoc::DeleteContents() </p>
    <p>{</p>
    <p>// TODO: Add your specialized code here and/or call the base class</p>
    <p> </p>
    <p>nLineNum=0;</p>
    <p>/*删除集合类的数据:</p>
    <p>用GetHeadPosition和GetNext遍历并用delete删除其中的数据,然后调 用RemoveAll()删除链表所包含的指针</p>
    <p>*/</p>
    <p>POSITION pos;</p>
    <p>pos=lines.GetHeadPosition();</p>
    <p>while(pos!=NULL)</p>
    <p>{</p>
    <p>((CString)lines.GetNext(pos)).Empty();</p>
    <p>//调用CString的Empty()方法清除文本行的数据,对于其它类型的对 //象,应当调用delete 
    删除该对象</p>
    <p>}</p>
    <p>lines.RemoveAll();</p>
    <p>CDocument::DeleteContents();</p>
    <p>}</p>
    <p> </p>
    <p>编辑器的DeleteContents()实现与OnNewDocument()基本相同,别的程序则可能会有所不同。</p>
    <p>CDocument::OnOpenDocument成员函数在调用DeleteContents()函数后,将文档修改标记设置为FALSE(未修改),然后调用Serialize进行文档的串行化工作。</p>
    <p> </p>
    <p></font><font color="#3973DE" size="3">读写文档——串行化</font><font SIZE="3"></p>
    <p>文档对象的串行化是指对象的持续性,即对象可以将其当前状态,由其成员变量的值表示,写入到永久性存储体(通常是指磁盘)中。下次则可以从永久性存储体中读取对象的状态,从而重建对象。这种对象的保存和恢复的过程称为串行化。对象的可持续性允许你将一个复杂的对象网络保存到永久性存储体中,从而在对象从内存中删去后仍保持它们的状态。以后,可以从永久性存储器中载入对象并在内存中重载。保存和载入可持续化、串行化的数据通过CArchive对象作为中介来完成。</p>
    <p>文档的串行化在Serialize成员函数中进行。当用户选择File Save、Save 
    As或Open命令时,都会自动执行这一成员函数。AppWizard只给出了一个Serialze()函数的框架,读者要做的时定制这个Serialize函数。Serialize()函数由一个简单的if-else语句组成:</p>
    <p>void CEditorDoc::Serialze(CArchive&amp; ar)</p>
    <p>{</p>
    <p>if(ar.IsStoring())</p>
    <p>{</p>
    <p>//TODO: add storing code here.</p>
    <p>}</p>
    <p>else</p>
    <p>{</p>
    <p>//TODO: add loading code here.</p>
    <p>}</p>
    <p>}</p>
    <p>在框架中,Serialize函数的参数ar是一个CArchive类型对象,它包含一个CFile类型的文件指针(类似于C语言的文件指针),执行一个文件。CArchive对象为读写CFile(文件类)对象中的可串行化数据提供了一种类型安全的缓冲机制。通常CFile代表一个磁盘文件;但它也可以是一个内存文件(CMemFile对象)或剪贴板。一个给定的CArchive对象只能读数据或写数据,而不能同时读写数据。当保存数据到archive对象中时,archive把它放在一个缓冲区中。直到缓冲区满,才把数据写入它所包含的文件指针指向的CFile对象中。同样的,当从archive对象读数据时,archive对象从文件中读取内容到缓冲区,然后再从缓冲区读入到可串行化的对象中。这种缓冲机制减少了访问物理磁盘的次数,从而提高了应用程序的性能。</p>
    <p>在创建和使用一个CArchive对象之前,必须先创建一个CFile文件类对象。而且还必须确保archive的载入和保存状态同文件打开模式相兼容。幸运的是,应用程序框架已经为我们做好了这些工作。</p>
    <p ALIGN="JUSTIFY">当应用程序响应File-&gt;Open、File-Save和File-Save As命令时,应用程序框架都会通过调用CDocument成员函数(对于File-&gt;Open调用OnOpenDocument,对于File-&gt;Save和File-&gt;Save 
    As调用OnSaveDocument)创建CFile对象,并以适当的方式打开文件,对于File-&gt;Open是打开文件并读,对于Save和SaveAs是打开文件并写。然后框架会自动把文件对象连接到一个CArchive对象上,并设置CArchive的读写方式。</p>
    <p>在Editor的Serialize()函数体内,我们看到CArchive对象有一个IsStoring()成员函数。该成员函数告诉串行化函数是需要写入还是读取串行数据。如果数据要写入(Save或Save 
    As),IsStoring()返回布尔值TRUE;如果数据是被读取,则返回FALSE。</p>
    <p>现在添加串行化操作代码,实现编辑器文档的读写功能。修改后的Serialize()函数形式如清单7.6。</p>
    <p>清单7.6 CEditorDoc的串行化方法</p>
    <p>/////////////////////////////////////////////////////////////////////////////</p>
    <p>// CEditorDoc serialization</p>
    <p>void CEditorDoc::Serialize(CArchive&amp; ar)</p>
    <p>{</p>
    <p> </p>
    <p>CString s(&quot;&quot;);</p>
    <p>int nCount=0;</p>
    <p>CString item(&quot;&quot;);</p>
    <p> </p>
    <p>if (ar.IsStoring())</p>
    <p>{</p>
    <p> </p>
    <p>POSITION pos;</p>
    <p> </p>
    <p>pos=lines.GetHeadPosition();</p>
    <p> </p>
    <p>if(pos==NULL)</p>
    <p>{</p>
    <p>return;</p>
    <p>}</p>
    <p>while(pos!=NULL)</p>
    <p>{</p>
    <p>item=lines.GetNext(pos);</p>
    <p>ar&lt;&lt;item;</p>
    <p>item.Empty();//clear the line buffer</p>
    <p>}</p>
    <p>}</p>
    <p>else</p>
    <p>{</p>
    <p>// TODO: add loading code here</p>
    <p> </p>
    <p>while(1)</p>
    <p>{</p>
    <p>try{</p>
    <p>ar&gt;&gt;item;</p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -