Storing and Loading CObjects via an Archive

Storing and loading CObjects via an archive requires extra consideration. In certain cases, you should call the Serialize function of the object, where the CArchive object is a parameter of the Serialize call, as opposed to using the << or >> operator of the CArchive. The important fact to keep in mind is that the CArchive >> operator constructs the CObject in memory based on CRuntimeClass information previously written to the file by the storing archive.

Therefore, whether you use the CArchive << and >> operators, versus calling Serialize, depends on whether you need the loading archive to dynamically reconstruct the object based on previously stored CRuntimeClass information. Use the Serialize function in the following cases:

  • When deserializing the object, you know the exact class of the object beforehand.

  • When deserializing the object, you already have memory allocated for it.

Caution

If you load the object using the Serialize function, you must also store the object using the Serialize function. Don't store using the CArchive << operator and then load using the Serialize function, or store using the Serialize function and then load using CArchive >> operator.

The following example illustrates the cases:

class CMyObject : public CObject
{
   // ...Member functions
public:
   CMyObject() {}
   virtual void Serialize(CArchive &ar);

   // Implementation
protected:
   DECLARE_SERIAL(CMyObject)
};

class COtherObject : public CObject
{
   // ...Member functions
public:
   COtherObject() {}
   virtual void Serialize(CArchive &ar);

   // Implementation
protected:
   DECLARE_SERIAL(COtherObject)
};

class CCompoundObject : public CObject
{
   // ...Member functions
public:
   CCompoundObject();
   ~CCompoundObject();
   virtual void Serialize(CArchive &ar);

   // Implementation
protected:
   CMyObject m_myob;       // Embedded object
   COtherObject *m_pOther; // Object allocated in constructor
   CObject *m_pObDyn;      // Dynamically allocated object
   //..Other member data and implementation

   DECLARE_SERIAL(CCompoundObject)
};

 

IMPLEMENT_SERIAL(CMyObject, CObject, 1)
IMPLEMENT_SERIAL(COtherObject, CObject, 1)
IMPLEMENT_SERIAL(CCompoundObject, CObject, 1)

CCompoundObject::CCompoundObject()
{
   m_pOther = new COtherObject; // Exact type known and object already
                                //allocated.
   m_pObDyn = NULL;             // Will be allocated in another member function
                                // if needed, could be a derived class object.
}

CCompoundObject::~CCompoundObject()
{
   delete m_pOther;
}

void CCompoundObject::Serialize(CArchive &ar)
{
   CObject::Serialize(ar);  // Always call base class Serialize.
   m_myob.Serialize(ar);    // Call Serialize on embedded member.
   m_pOther->Serialize(ar); // Call Serialize on objects of known exact type.

   // Serialize dynamic members and other raw data
   if (ar.IsStoring())
   {
      ar << m_pObDyn;
      // Store other members
   }
   else
   {
      ar >> m_pObDyn; // Polymorphic reconstruction of persistent object
      //load other members
   }
}

In summary, if your serializable class defines an embedded CObject as a member, you should not use the CArchive << and >> operators for that object, but should call the Serialize function instead. Also, if your serializable class defines a pointer to a CObject (or an object derived from CObject) as a member, but constructs this other object in its own constructor, you should also call Serialize.

See also

Serialization: Serializing an Object