29.6 The cCritter serialize
All the tricks we've discussed come into play with the cCritter::Serialize.
A critter has a cCritter* _ptarget field that's a reference pointer, so when you save and reload the game file, which means saving and loading all of the critters, then the copy of the target critter is not going to be loaded into the exact same area of memory, and the old _ptarget pointer will no longer be correct. That's why we have the _targetindex field in the cCritter.
This reference pointer problem comes back in even stronger form with the armed critters, who have pointers to their bullets. You can look at the Serialize
code inside the critterarmed.cpp file to see one way to work things out. In particular look for the fixPointerRefs
methods.
The cCritter
also has member pointer fields. As we explained in the last section, in order to load the pointer _psprite and _plistener fields of cCritter, we must first delete
them and then use the overloaded operator>>
to overwrite them with new pointers.
Note also that the CArray
_forcearray has to be handled with Serialize
and that, to avoid having a possible memory leak, you have to empty it out before you read into it. Here's some relevant code.
void cCritter::Serialize(CArchive &ar)
{
CObject::Serialize(ar);
//Call the base class method to save the CRuntimeClass info.
if (ar.IsStoring()) //Writing data.
{
_forcearray.Serialize(ar);
//Generally you cant use << with a CArray.
//Sprite
ar << _psprite <<
//Listener
_plistener <<
//Personal variables
_age << _lasthit_age<< _oldrecentlydamaged << _health <<
_shieldflag <<
//........ETCETERA..........
//Pointer index variables.
if (_pownerbiota)
_targetindex = _pownerbiota->_index(_ptarget);
//Prepare for a pointer reference.
else
_targetindex = cBiota::NOINDEX;
ar << _targetindex;
}
else //Reading data.
{
clearForcearray(); /* We have to empty out the array before
reading into it or we'll have a memory leak in case
something's in it. */
delete _psprite;
/* always delete a pointer before reading into it or you
have a leak. */
delete _plistener;
/* always delete a pointer before reading into it or you
have a leak. */
_forcearray.Serialize(ar); /* Read in. You usually can't use >>
with a CArray. */
//Sprite
ar >> _psprite >> /* Uses CreateObject to creates a new
cSprite* object of the correct child class, copies the new
object's fields out of the file, and places the pointer to
the new object in _psprite */
//Listener and force
_plistener >> // See the comment just above.
//Personal variables
_age >> _lasthit_age >> _oldrecentlydamaged >> _health >>
_shieldflag >>
//........ETCETERA..........
//Index for pointer reference variable.
ar >> _targetindex; /* cBiota::Serialize will call
cCritter::FixPointerRefs to replace this index
by a pointer. */
_ptarget = NULL; /* The cBiota::Serialize will call
all pcritter->fixPointerRefs for each critter to fix the
_ptarget, and also fix any pointer refs in the forces. */
}
}
The cCritter::fixPointerRefs
can only be called after all of the cCritter
have been loaded and added to the cBiota
array, with this array installed as the _pownerbiota of each cCritter. The fixPointerRefs
does the following, in part.
void cCritter::fixPointerRefs()
{
if (!_pownerbiota)
return;
else
_ptarget = _pownerbiota->GetAt(_targetindex);
}
|