The File Demo 2 Application
The next sample application, File Demo 2, demonstrates the steps you take to
create a class from which you can create persistent objects. It will have an Edit,
Change Messages command that changes all three strings. Like File Demo, it will
save and reload the document when the user chooses File, Save or File, Open.
Build an SDI application called MultiString just as you built File Demo. Add a
member variable to the document, as before, so that the Attributes section of
MultiStringDoc.h reads
// Attributes
public:
CMessages m_messages;
The next step is to write the CMessages class.
Looking at the CMessages Class
Before you can understand how the document class manages to save and load its
contents successfully, you have to understand how the CMessages class, of which
the document class's m_messages data member is an object, works. As you work with
this class, you will see how to implement the preceding five steps for creating a
persistent class.
To create the CMessages class, first choose Insert, New Class. Change the class
type to generic class and name it CMessages. In the area at the bottom of the
screen, enter CObject as the base class name and leave the As column set to public,
as shown in Figure 7.3.
FIG. 7.3 Create a new class to hold the messages.
This will create two files: messages.h for the header and messages.cpp for the
code. It also adds some very simple code to these files for you. (You may get a
warning about not being able to find the header file for CObject: just click OK
and ignore it because CObject, like all MFC files, is available to you without
including extra headers.)
Switch back to Multistringdoc.h and add this line before the class definition:
#include "Messages.h"
This will ensure the compiler knows about the CMessages class when it compiles
the document class. You can build the project now if you want to be sure you
haven't forgotten anything. Now switch back to Messages.h and add these lines:
DECLARE_SERIAL(CMessages)
protected:
CString m_message1;
CString m_message2;
CString m_message3;
public:
void SetMessage(UINT msgNum, CString msg);
CString GetMessage(UINT msgNum);
void Serialize(CArchive& ar);
The DECLARE_SERIAL() macro provides the additional function and member
variable declarations needed to implement object persistence.
Next come the class's data members, which are three objects of the CString class.
Notice that they are protected member variables. The public member functions
are next. SetMessage(), whose arguments are the index of the string to set and the
string's new value, changes a data member. GetMessage() is the complementary
function, enabling a program to retrieve the current value of any of the strings.
Its single argument is the number of the string to retrieve.
Finally, the class overrides the Serialize() function, where all the data saving and
loading takes place. The Serialize() function is the heart of a persistent object,
with each persistent class implementing it in a different way. Listing 7.6 shows the
code for each of these new member functions. Add it to messages.cpp.
Listing 7.6 MESSAGES.CPP - The CMessages Class Implementation File
void CMessages::SetMessage(UINT msgNum, CString msg)
{
switch (msgNum)
{
case 1:
m_message1 = msg;
break;
case 2:
m_message2 = msg;
break;
case 3:
m_message3 = msg;
break;
}
SetModifiedFlag();
}
CString CMessages::GetMessage(UINT msgNum)
{
switch (msgNum)
{
case 1:
return m_message1;
case 2:
return m_message2;
case 3:
return m_message3;
default:
return "";
}
}
void CMessages::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar << m_message1 << m_message2 << m_message3;
}
else
{
ar >> m_message1 >> m_message2 >> m_message3;
}
}
There is nothing tricky about the SetMessage() and GetMessage() functions, which
perform their assigned tasks precisely. The Serialize() function, however, may
inspire a couple of questions. First, note that the first line of the body of the
function calls the base class's Serialize() function. This is a standard practice for
many functions that override functions of a base class. In this case, the call to
CObject::Serialize() does not do much because the CObject class's Serialize()
function is empty. Still, calling the base class's Serialize() function is a good habit
to get into because you may not always be working with classes derived directly
from CObject.
After calling the base class's version of the function, Serialize() saves and loads
its data in much the same way a document object does. Because the data members
that must be serialized are CString objects, the program can use the >> and <<
operators to write the strings to the disk.
Towards the top of messages.cpp, after the include statements, add this line:
IMPLEMENT_SERIAL(CMessages, CObject, 0)
The IMPLEMENT_SERIAL() macro is partner to the DECLARE_SERIAL() macro,
providing implementation for the functions that give the class its persistent
capabilities. The macro's three arguments are the name of the class, the name of
the immediate base class, and a schema number, which is like a version number. In
most cases, you use 0 or 1 for the schema number.
No comments:
Post a Comment