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); }