29.5 Serializing pointers


Serializing pointer members

CPopDoc has a cGame* _pgame object as its most important member. Serializing the cGame *_pgame pointer takes a bit of care.

Something to realize is that when you load into a CPopDoc , that CPopDoc object will already exist, so it will have been initialized by a constructor call. So the _pgame will in fact be a valid pointer. Whenever you load into a valid pointer variable ptr , you have to call delete on the pointer first, otherwise you'll have a memory leak caused by the 'orphaned' object that the pointer pointed to before you overwrote it with the load. For reasons we'll now explain, we must use an overloaded ar >> ptr operator to load into a pointer, rather than a call like ptr->Serialize(ar).

To save and load the _pgame fields of CPopDoc , we use the autogenerated overloaded operator<<(CArchive &ar, cgame *p) and operator>>(CArchive &ar, cGame *&p) . MFC has 'written the code' for these operators automatically because the cGame

  • inherits from CObject ,

  • has DECLARE_SERIAL and IMPLEMENT_SERIAL, and

  • has its own Serialize defined

In the load case we want to make a new cGame * and place it into the _pgame field, and this is exactly what ar >> _pgame does.

Now, as mentioned just above, in the load case, we delete _pgame before loading it. At first you might think you could load either with _pgame->Serialize(ar) or ar >> _pgame . But since you delete _pgame just before the load, it becomes an invalid pointer just before the load, and you would get a crash if you tried to call _pgame->Serialize(ar) for the load. We could actually use _pgame->Serialize(ar) in the save case, but for symmetry in the appearance of the read and write cases, we use ar << _pgame there.

Here's a partial listing of the cPopDoc::Serialize .

 void CPopDoc::Serialize(CArchive& ar)  {      CObject::Serialize(ar);      if (ar.IsStoring()) // Save          ar << _pgame;      else //Load      {          delete _pgame; /*At CPopDoc construction a document creates a              default cGame *_pgame. So if we're loading a game we need              to delete the existing game first or there will be a              memory leak.*/          ar >> _pgame; /* Uses CreateObject to creates a new cGame*              object of the correct child class, copies the new objects              fields out of the file, and places the pointer to the new              object in _pgame. */          _pgame->setGameover(TRUE); /* So you can press ENTER to              actually start it running. _brandnewgameflag will have              been set to TRUE by the constructor call inside the ar >>              call, so the first ENTER won't randomize things. */          UpdateAllViews(NULL, CPopDoc::VIEWHINT_STARTGAME, 0);      }  } 

Serializing reference pointers

One exception to the principle of 'serialize everything in sight' is when your objects have pointer members that are used as references to point to other objects that may or may not be getting serialized as well. This is, in other words, a case where our code actually has two or more copies of the same pointer in two different locations. One of these copies is the 'member' and this copy gets serialized as just described. But the other copies are meant only to echo the address value of the member pointer object. In these cases we need to do something a little tricky.

The cGame class, for instance, has a separate cCritter* _pplayer pointer that is the same value as one of the cCritter * actually in the cBiota *_pbiota member. We track the index of where it appears in the cBiota array, if it does appear, and we save that. Here's some of the relevant code.

 void cGame::Serialize(CArchive& ar)  {      int playerindex;      CObject::Serialize(ar);          /*It's worth noting that when we call this next line in              loading mode, the _pbiota will be pointing in a non-NULL              cBiota that was created by the cGame constructor, so we'll              need to have the cBiota::Serialize take care of deleting              members of an existing cBiota before loading into it. */      _pbiota->Serialize(ar);      if (ar.IsStoring()) // Save      {          playerindex = _index(_pplayer);          ar << _border << /* ETCETERA */ << playerindex;      }      else //Load      {          ar >> _border >> /* ETCETERA */ >> playerindex;              /* _pplayer currently equals NULL or one of the old                  dummy pointers in the cBiota, either way we don't have                  to delete it. Remember it's only a reference                  copy. */          _pplayer = _pbiota->GetAt(playerindex);      }  } 


Software Engineering and Computer Games
Software Engineering and Computer Games
ISBN: B00406LVDU
EAN: N/A
Year: 2002
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net