📄 ch13.htm
字号:
Your Own Classes and Modules." The other things that you need to do are giveyour class a name and specify the base class from which it will be inherited.</P><P>For your sample application, because the form that you created has informationabout a person, you might want to call your class something like CPerson. To be ableto hold your class in the object array, you need to give it CObject as the base class.Just like on Day 10, the New Class dialog will claim that it cannot find the headerwith the base class in it and that you need to add this. Well, it's already included,so you can ignore this message. (On Day 16, you'll learn when you need to pay attentionto this message.)</P><P>Once you create your new class, you'll need to add the variables for holding thedata elements that will be displayed on the screen for the user. Following good object-orienteddesign, these variables will all be declared as private variables, where they cannotbe directly manipulated by other classes. The variable types should match the variabletypes of the variables that are attached to the window controls in the view class.</P><P>With the sample application you are creating, you need to add the variables inTable 13.3.</P><P><H4>TABLE 13.3. CLASS VARIABLES FOR THE CPerson CLASS.</H4><P><TABLE BORDER="1"> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT"> <H4>Name </TD> <TD ALIGN="LEFT"> <H4>Type </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_bEmployed </TD> <TD ALIGN="LEFT">BOOL </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_iAge </TD> <TD ALIGN="LEFT">int </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_sName </TD> <TD ALIGN="LEFT">CString </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_iMaritalStatus </TD> <TD ALIGN="LEFT">int </TD> </TR></TABLE><BLOCKQUOTE> <P><HR><STRONG>NOTE:</STRONG> An inline function is a short C++ function in which, when the application is being compiled, the function body is copied in place of the function call. As a result, when the compiled application is running, the function code is executed without having to make a context jump to the function and then jump back once the function has completed. This reduces the overhead in the running application, increasing the execution speed slightly, but also makes the resulting executable application slightly larger. The more places the inline function is called, the larger the application will eventually get. For more information on inline functions, consult Appendix A, "C++ Review."<HR></BLOCKQUOTE><H4>Adding Methods for Reading and Writing Variables</H4><P>Once you create your class, you need to provide a means for reading and writingto the variables in the class. One of the easiest ways to provide this functionalityis to add inline functions to the class definition. You create a set of inline functionsto set each of the variables and then make another set for retrieving the currentvalue of each variable.</P><P>If you want to implement the Get and Set variable functions for your CPerson classin the sample application that you are building, edit the Person.h header file, addingthe lines in Listing 13.4.</P><P><H4>LISTING 13.4. THE Get AND Set INLINE FUNCTION DECLARATIONS.</H4><PRE> 1: class CPerson : public CObject 2: { 3: public: 4: // Functions for setting the variables 5: void SetEmployed(BOOL bEmployed) { m_bEmployed = bEmployed;} 6: void SetMaritalStat(int iStat) { m_iMaritalStatus = iStat;} 7: void SetAge(int iAge) { m_iAge = iAge;} 8: void SetName(CString sName) { m_sName = sName;} 9: // Functions for getting the current settings of the variables10: BOOL GetEmployed() { return m_bEmployed;}11: int GetMaritalStatus() { return m_iMaritalStatus;}12: int GetAge() {return m_iAge;}13: CString GetName() {return m_sName;}14: CPerson();15: virtual ~CPerson();16: 17: private:18: BOOL m_bEmployed;19: int m_iMaritalStatus;20: int m_iAge;21: CString m_sName;22: };</PRE><P>After you have the methods for setting and retrieving the values of the variablesin your custom class, you'll probably want to make sure that the variables are initializedwhen the class is first created. You can do this in the class constructor by settingeach of the variables to a default value. For instance, in your sample application,you add the code in Listing 13.5 to the constructor of the CPerson class.</P><P><H4>LISTING 13.5. THE CPerson CONSTRUCTOR.</H4><PRE>1: CPerson::CPerson()2: {3: // Initialize the class variables4: m_iMaritalStatus = 0;5: m_iAge = 0;6: m_bEmployed = FALSE;7: m_sName = "";8: }</PRE><P><H4>Serializing the Class</H4><P>After you have your custom class with all variables defined and initialized, youneed to make the class serializable. Making your class serializable involves threesteps. The first step is adding the Serialize function to the class. This functionwrites the variable values to, and reads them back from, the CArchive object usingC++ streams. The other two steps consist of adding the DECLARE_SERIAL and IMPLEMENT_SERIALmacros. Once you add these elements, your custom class will be serializable and readyfor your application.</P><P>To add the Serialize function to your custom class, add a member function throughthe Class View tab in the workspace pane. Specify the function type as void, thefunction declaration as Serialize(CArchive &ar), and the access as public andcheck the Virtual check box. This should add the Serialize function and place youin the editor, ready to flesh out the function code.</P><P>In the Serialize function, the first thing you want to do is to call the ancestor'sSerialize function. When you call the ancestor's function first, any foundation informationthat has been saved is restored first, providing the necessary support for your classbefore the variables in your class are restored. Once you call the ancestor function,you need to determine whether you need to read or write the class variables. Youcan do this by calling CArchive's IsStoring method. This function returns TRUE ifthe archive is being written to and FALSE if it's being read from. If the IsStoringfunction returns TRUE, you can use C++ I/O streams to write all your class variablesto the archive. If the function returns FALSE, you can use C++ streams to read fromthe archive. In both cases, you must be certain to order the variables in the sameorder for both reading and writing. If you need more information about C++ streams,see Appendix A.</P><P>An example of a typical Serialize function for your sample custom class is shownin Listing 13.6. Notice that the CPerson variables are in the same order when writingto and reading from the archive.</P><P><H4>LISTING 13.6. THE CPerson.Serialize FUNCTION.</H4><PRE> 1: void CPerson::Serialize(CArchive &ar) 2: { 3: // Call the ancestor function 4: CObject::Serialize(ar); 5: 6: // Are we writing? 7: if (ar.IsStoring()) 8: // Write all of the variables, in order 9: ar << m_sName << m_iAge << m_iMaritalStatus << m_bEmployed;10: else11: // Read all of the variables, in order12: ar >> m_sName >> m_iAge >> m_iMaritalStatus >> m_bEmployed;13: }</PRE><P>Once you have the Serialize function in place, you need to add the macros to yourcustom class. The first macro, DECLARE_SERIAL, needs to go in the class header andis passed the class name as its only argument.</P><P>For example, to add the DECLARE_SERIAL macro to the custom CPerson class in yoursample application, you add the macro just below the start of the class declaration,where it will receive the default access for the class. You specify the class name,CPerson, as the only argument to the macro, as in Listing 13.7.</P><BLOCKQUOTE> <P><HR><STRONG>NOTE:</STRONG> The default access permission for functions and variables in C++ classes is public. All functions and variables that are declared before the first access declaration are public by default. You could easily add all of the public class functions and variables in this area of the class declaration, but explicitly declaring the access permission for all functions and variables is better practice--because that way, there is little to no confusion about the visibility of any of the class functions or variables.<HR><BR> </P> <P><HR><STRONG>NOTE:</STRONG> Most C++ functions need a semicolon at the end of the line of code. The two serialization macros do not, due to the C preprocessor, which replaces each of the macros with all of the code before compiling the application. It doesn't hurt to place the semicolons there; they are simply ignored.<HR></BLOCKQUOTE><H4>LISTING 13.7. THE SERIALIZED CPerson CLASS DECLARATION.</H4><PRE> 1: class CPerson : public CObject 2: { 3: DECLARE_SERIAL (CPerson) 4: public: 5: // Functions for setting the variables 6: void SetEmployed(BOOL bEmployed) { m_bEmployed = bEmployed;} 7: void SetMaritalStat(int iStat) { m_iMaritalStatus = iStat;} 8: void SetAge(int iAge) { m_iAge = iAge;} 9: void SetName(CString sName) { m_sName = sName;}10: // Functions for getting the current settings of the variables11: BOOL GetEmployed() { return m_bEmployed;}12: int GetMaritalStatus() { return m_iMaritalStatus;}13: int GetAge() {return m_iAge;}14: CString GetName() {return m_sName;}15: CPerson();16: virtual ~CPerson();17: 18: private:19: BOOL m_bEmployed;20: int m_iMaritalStatus;21: int m_iAge;22: CString m_sName;23: };</PRE><BLOCKQUOTE> <P><HR><STRONG>NOTE:</STRONG> In practice, if you read a file that was written using a previous version of the Serialize function in your class, your application will raise an exception, which you can then catch using standard C++ exception-handling techniques. This allows you to add code to your application to recognize and convert files created with earlier versions of your application. For information on C++ exception handling, see Appendix A.<HR></BLOCKQUOTE><P>To complete the serialization of your custom class, you need to add the IMPLEMENT_SERIAL macro to the class definition. The best place to add this macro is beforethe constructor definition in the CPP file containing the class source code. Thismacro takes three arguments: the custom class name, the base class name, and theversion number. If you make any changes to the Serialize function, you should incrementthe version number argument to the IMPLEMENT_SERIAL macro. This version number indicateswhen a file was written using a previous version of the Serialize function and thusmay not be readable by the current version of the application.</P><P>To add the IMPLEMENT_SERIAL macro to your sample application, add it into thePerson.cpp file just before the CPerson class constructor. Pass CPerson as the firstargument (the class name), CObject as the second argument (the base class), and 1as the version number, as in Listing 13.8.</P><P><H4>LISTING 13.8. THE IMPLEMENT_SERIAL MACRO IN THE CPerson CODE.</H4><PRE> 1: // Person.cpp: implementation of the CPerson class. 2: // 3: ////////////////////////////////////////////////////////////////////// 4: 5: #include "stdafx.h" 6: #include "Serialize.h" 7: #include "Person.h" 8: 9: #ifdef _DEBUG10: #undef THIS_FILE11: static char THIS_FILE[]=__FILE__;12: #define new DEBUG_NEW13: #endif14: 15: IMPLEMENT_SERIAL (CPerson, CObject, 1)16: //////////////////////////////////////////////////////////////////////17: // Construction/Destruction18: //////////////////////////////////////////////////////////////////////19: 20: CPerson::CPerson()21: {22: // Initialize the class variables23: m_iMaritalStatus = 0;24: m_iAge = 0;25: m_bEmployed = FALSE;26: m_sName = "";27: }</PRE><P><H3><A NAME="Heading9"></A>Building Support in the Document Class</H3><P>When you build a form-based application, where the form on the window is the primaryplace for the user to interact with the application, there is an unstated assumptionthat your application will allow the user to work with a number of records. Thismeans that you need to include support for holding and navigating these records.The support for holding the records can be as simple as adding an object array asa variable to the document class, as you did back on Day 10. This allows you to addadditional record objects as needed. The navigation could be a number of functionsfor retrieving the first, last, next, or previous record objects. Finally, you needinformational functionality so that you can determine what record in the set theuser is currently editing.</P><P>To hold and support this functionality, the document class will probably needtwo variables, the object array and the current record number in the array. Thesetwo variables will provide the necessary support for holding and navigating the recordset.</P><P>For your example, add the two variables for supporting the record set of CPersonobjects as listed in Table 13.4. Specify private access for both variables.</P><P><H4>TABLE 13.4. DOCUMENT CLASS VARIABLES.</H4><P><TABLE BORDER="1">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -