📄 ch22.htm
字号:
<TT>}</TT>
<TT>void CUser::Set( const CString& szName, const CString& szAddr )</TT>
<TT>{</TT>
<TT> m_szName = szName;</TT>
<TT> m_szAddr = szAddr;</TT>
<TT>}</TT>
<TT>CString CUser::GetName() const</TT>
<TT>{</TT>
<TT> return m_szName;</TT>
<TT>}</TT>
<TT>CString CUser::GetAddr() const</TT>
<TT>{</TT>
<TT> return m_szAddr;</TT>
<TT>}</TT>
</FONT></PRE>
<H3><FONT COLOR="#000077"><B>Overriding the <TT>Serialize</TT> Function</B></FONT></H3>
<P>Every persistent class must implement a <TT>Serialize</TT> member function, which
is called in order to serialize or deserialize an object. The single parameter for
<TT>Serialize</TT> is the <TT>CArchive</TT> object used to load or store the object.
The version of <TT>Serialize</TT> used by the <TT>CUser</TT> class is shown in Listing
22.4; add this function to the <TT>Users.cpp</TT> source file.
<H4><FONT COLOR="#000077">TYPE: Listing 22.4. The CUser::Serialize member function.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>void CUser::Serialize( CArchive& ar )</TT>
<TT>{</TT>
<TT> if( ar.IsLoading() )</TT>
<TT> {</TT>
<TT> ar >> m_szName >> m_szAddr;</TT>
<TT> }</TT>
<TT> else</TT>
<TT> {</TT>
<TT> ar << m_szName << m_szAddr;</TT>
<TT> }</TT>
<TT>}</TT>
</FONT></PRE>
<H3><FONT COLOR="#000077"><B>Creating a Serialized Collection</B></FONT></H3>
<P>You can serialize most MFC collection classes, enabling large amounts of information
to be stored and retrieved easily. For example, you can serialize a <TT>CArray</TT>
collection by calling its <TT>Serialize</TT> member function. As with the other MFC
template-based collection classes, you cannot use the insertion and extraction operators
with <TT>CArray</TT>.</P>
<P>By default, the template-based collection classes perform a bitwise write when
serializing a collection and a bitwise read when deserializing an archive. This means
that the data stored in the collection is literally written, bit by bit, to the archive.
Bitwise serialization is a problem when you use collections to store pointers to
objects. For example, the Customers project uses the <TT>CArray</TT> class to store
a collection of <TT>CUser</TT> objects. The declaration of the <TT>CArray</TT> member
is as follows:</P>
<PRE><FONT COLOR="#0066FF"><TT>CArray<CUser*, CUser*&> m_setOfUsers;</TT>
</FONT></PRE>
<P>Because the <TT>m_setOfUsers</TT> collection stores <TT>CUser</TT> pointers, storing
the collection using a bitwise write will only store the current addresses of the
contained objects. This information becomes useless when the archive is deserialized.</P>
<P>Most of the time, you must implement a helper function to assist in serializing
a template-based collection. Helper functions don't belong to a class; they are global
functions that are overloaded based on the function signature. The helper function
used when serializing a template is <TT>SerializeElements</TT>. Figure 22.2 shows
how you call the <TT>SerializeElements</TT> function to help serialize items stored
in a collection.</P>
<P><A NAME="02"></A><A HREF="02.htm"><B>Figure 22.2.</B> </A><I><BR>
The <TT>SerializeElements</TT> helper function.</I></P>
<P>A version of <TT>SerializeElements</TT> used with collections of <TT>CUser</TT>
objects is provided in List- ing 22.5.
<H4><FONT COLOR="#000077">TYPE: Listing 22.5. The SerializeElements function.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>void AFXAPI SerializeElements( CArchive& ar,</TT>
<TT> CUser** pUser,</TT>
<TT> int nCount )</TT>
<TT>{</TT>
<TT> for( int i = 0; i < nCount; i++, pUser++ )</TT>
<TT> {</TT>
<TT> if( ar.IsStoring() )</TT>
<TT> {</TT>
<TT> (*pUser)->Serialize(ar);</TT>
<TT> }</TT>
<TT> else</TT>
<TT> {</TT>
<TT> CUser* pNewUser = new CUser;</TT>
<TT> pNewUser->Serialize(ar);</TT>
<TT> *pUser = pNewUser;</TT>
<TT> }</TT>
<TT> }</TT>
<TT>}</TT>
</FONT></PRE>
<P>The <TT>SerializeObjects</TT> function has three parameters:
<UL>
<LI>A pointer to a <TT>CArchive</TT> object, as with <TT>Serialize</TT>.<BR>
<BR>
<LI>The address of an object stored in the collection. In this example, pointers
to <TT>CUser</TT> are stored in a <TT>CArray</TT>, so the parameter is a pointer
to a <TT>CUser</TT> pointer.<BR>
<BR>
<LI>The number of elements to be serialized.
</UL>
<P>In this example, when you're serializing objects to the archive, each <TT>CUser</TT>
object is individually written to the archive. When you're deserializing objects,
a new <TT>CUser</TT> object is created, and that object is deserialized from the
archive. The collection stores a pointer to the new object.
<H2><FONT COLOR="#000077"><B>What Is Document/View Serialization?</B></FONT></H2>
<P>The Document/View architecture uses serialization to save or open documents. When
a document is saved or loaded, the MFC framework in cooperation with the application's
document class creates a <TT>CArchive</TT> object and serializes the document to
or from storage.</P>
<P>The <TT>CDocument</TT> member functions required to perform serialization in a
Document/View application are mapped onto the New, Open, Save, and Save As commands
available from the File menu. These member functions take care of creating or opening
a document, tracking the modification status of a document, and serializing it to
storage.</P>
<P>When documents are loaded, a <TT>CArchive</TT> object is created for reading,
and the archive is deserialized into the document. When documents are saved, a <TT>CArchive</TT>
object is created for writing, and the document is written to the archive. At other
times, the <TT>CDocument</TT> class tracks the current modification status of the
document's data. If the document has been updated, the user is prompted to save the
document before closing it.</P>
<P>The Document/View support for serialization greatly simplifies the work required
to save and load documents in a Windows program. For a typical program that uses
persistent objects, you must supply only a few lines of source code to receive basic
support for serialization in a Document/View program. The Customers project has about
a page of Document/View source code; most of it is for handling input and output
required for the example.</P>
<P>The routines used by <TT>CArchive</TT> for reading and writing to storage are
highly optimized and have excellent performance, even when you're serializing many
small data objects. In most cases, it is difficult to match both the performance
and ease of use that you get from using the built-in serialization support offered
for Document/View applications.
<H2><FONT COLOR="#000077"><B>How Are Document/View Applications Serialized?</B></FONT></H2>
<P>As discussed in Hour 9, "The Document/View Architecture," data stored
in a Document/View application is contained by a class derived from <TT>CDocument</TT>.
This class also is responsible for controlling the serialization of all data contained
by the document class. This includes tracking modifications to the document so that
the program can display a warning before the user closes an unsaved document.</P>
<P>There are five phases in a document's life cycle:
<UL>
<LI>Creating a new document
<LI>Modifying the document
<LI>Storing, or serializing, the document
<LI>Closing the document
<LI>Loading, or deserializing, the document
</UL>
<P>You learned about most of these phases in earlier hours. The following sections
discuss how each phase affects document serialization.
<H3><FONT COLOR="#000077"><B>Creating a New Document</B></FONT></H3>
<P>As discussed in Hour 9, you create MDI and SDI documents differently. An MDI application
creates a new <TT>CDocument</TT> class for every open document, whereas an SDI program
reuses a single document.</P>
<P>Both SDI and MDI applications call the <TT>OnNewDocument</TT> function to initialize
a document object. The default version of <TT>OnNewDocument</TT> calls the <TT>DeleteContents</TT>
function to reset any data contained by the document. ClassWizard can be used to
add a <TT>DeleteContents</TT> function to your document class. Most applications
can just add code to <TT>DeleteContents</TT> instead of overriding <TT>OnNewDocument</TT>.
<H3><FONT COLOR="#000077"><B>Storing a Document</B></FONT></H3>
<P>When the user saves a document by selecting File | Save, the <TT>CWinApp::OnFileSave</TT>
function is called. This function is almost never overridden; it's a good idea to
leave it alone because it calls the <TT>CDocument::OnOpenDocument</TT> function to
serialize the document's data. The default version of <TT>OnOpenDocument</TT> creates
a <TT>CArchive</TT> object and passes it to the document's <TT>Serialize</TT> member
function. Usually, you serialize the data contained in the document in the same way
that other member data was serialized earlier this hour. After the document's data
has been serialized, the dirty bit is cleared, marking the document as unmodified.
The steps involved in storing a document are shown in Figure 22.3.</P>
<P><A NAME="03"></A><A HREF="03.htm"><B>Figure 22.3.</B> </A><I><BR>
The major functions called when you store a document.</I></P>
<P>The default version of <TT>OnOpenDocument</TT> is sufficient for most applications.
However, if your application stores data in a different way--for example, in several
smaller files or in a database--you should override <TT>OnOpenDocument</TT>.</P>
<P>When the user selects Save As from the File menu, a Common File dialog box collects
filename information. After the user selects a filename, the program calls the same
<TT>CDocument</TT> functions, and the serialization process works as described previously.
<H3><FONT COLOR="#000077"><B>Closing a Document</B></FONT></H3>
<P>When the user closes a document, the MFC Document/View framework calls the document
object's <TT>OnCloseDocument</TT> member function, as shown in Figure 22.4. The default
version of this function checks the document to make sure that no unsaved changes
are lost by calling the <TT>IsModified</TT> function. If the user did not modify
the document object, <TT>DeleteContents</TT> is called to free the data stored by
the document, and all views for the document are closed.</P>
<P><A NAME="04"></A><A HREF="04.htm"><B>Figure 22.4.</B></A> <I><BR>
The major functions called when you close a document.</I></P>
<P>If the user made changes to the document, the program displays a message box that
asks the user whether the document's unsaved changes should be saved. If the user
elects to save the document, the <TT>Serialize</TT> function is called. The document
is then closed by calling <TT>DeleteContents</TT> and closing all views for the document.
<H3><FONT COLOR="#000077"><B>Loading a Document</B></FONT></H3>
<P>When you're loading a document, the MFC framework calls the document object's
<TT>OnOpenDocument</TT> function. The default version of this function calls the
<TT>DeleteContents</TT> member function and then calls <TT>Serialize</TT> to load,
or deserialize, the archive. The default version of <TT>OnOpenDocument</TT>, shown
in Figure 22.5, is sufficient for almost any application.
<H4><FONT COLOR="#000077">Modifying the Document Class</FONT></H4>
<P>The document class used in the Customers project has one new data member, a <TT>CArray</TT>
object that stores a collection of <TT>CUser</TT> pointers representing a customer
list. The document class also has two member functions used to access the array of
<TT>CUser</TT> pointers. Add declarations for <TT>m_setOfUsers</TT> and two member
functions to the <TT>CCustomersDoc</TT> class, as shown in List- ing 22.6.</P>
<P><A NAME="05"></A><A HREF="05.htm"><B>Figure 22.5.</B></A> <I><BR>
The major functions called when you open a document.</I>
<H4><FONT COLOR="#000077">TYPE: Listing 22.6. Adding a CArray member variable to
the CCustomersDoc class.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>// Attributes</TT>
<TT>public:</TT>
<TT> int GetCount() const;</TT>
<TT> CUser* GetUser( int nUser ) const;</TT>
<TT>protected:</TT>
<TT> CArray<CUser*, CUser*&> m_setOfUsers;</TT>
</FONT></PRE>
<P>You should make two other changes to the <TT>CustomersDoc.h</TT> header file.
First, because the <TT>CArray</TT> template <TT>m_setOfUsers</TT> is declared in
terms of <TT>CUser</TT> pointers, you must add an <TT>#include</TT> statement for
the <TT>Users.h</TT> file. Second, you use a version of the <TT>SerializeElements</TT>
helper function so you need a declaration of that global function. Add the source
code provided in Listing 22.7 to the top of <TT>CustomersDoc.h</TT>.
<H4><FONT COLOR="#000077">TYPE: Listing 22.7. Changes to the CustomersDoc.h header
file.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>#include "Users.h"</TT>
<TT>void AFXAPI SerializeElements( CArchive& ar,</TT>
<TT> CUser** pUser,</TT>
<TT> int nCount );</TT>
</FONT></PRE>
<P>Because the <TT>CCustomerDoc</TT> class contains a <TT>CArray</TT> member variable,
the template collection declarations must be included in the project. Add an <TT>#include</TT>
statement to the bottom of the <TT>StdAfx.h</TT> file:</P>
<PRE><FONT COLOR="#0066FF"><TT>#include "afxtempl.h"</TT>
</FONT></PRE>
<H4><FONT COLOR="#000077">Creating a Dialog Box</FONT></H4>
<P>The dialog box used to enter data for the Customers example is similar to dialog
boxes you created for previous examples. Create a dialog box that contains two edit
controls, as shown in Figure 22.6.</P>
<P><A NAME="06"></A><A HREF="06.htm"><B>Figure 22.6.</B></A> <BR>
<I>The dialog box used in the Customers sample project.</I></P>
<P>Give the new dialog box a resource ID of <TT>IDD_USER_DLG</TT>. The two edit controls
are used to add user names and email addresses to a document contained by the <TT>CCustomerDoc</TT>
class. Use the values from Table 22.1 for the two edit controls.
<H4><FONT COLOR="#000077">Table 22.1. Edit controls contained in the IDD_USER_DLG
dialog box.</FONT></H4>
<P>
<TABLE BORDER="1">
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP"><B>Edit Control</B></TD>
<TD ALIGN="LEFT" VALIGN="TOP"><B>Resource ID</B></TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP">Name</TD>
<TD ALIGN="LEFT" VALIGN="TOP"><TT>IDC_EDIT_NAME</TT></TD>
</TR>
<TR ALIGN="LEFT" rowspan="1">
<TD ALIGN="LEFT" VALIGN="TOP">Address</TD>
<TD ALIGN="LEFT" VALIGN="TOP"><TT>IDC_EDIT_ADDR</TT></TD>
</TR>
</TABLE>
</P>
<P>Using ClassWizard, add a class named <TT>CUsersDlg</TT> to handle the new dialog
box. Add two <TT>CString</TT> variables to the class using the values from Table
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -