Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


How to Create Persistent Objects-3 : Page 3

Step 2: Serializing Objects
To serialize a complete object, we write its data members to a file:

class MP3_clip { private: std::time_t date; std::string name; int bitrate; bool stereo; public: void serialize(); void deserialize(); //.. }; void MP3_clip::serialize() { { int size=name.size();// store name's length //empty file if it already exists before writing new data ofstream arc("mp3.dat", ios::binary|ios::trunc); arc.write(reinterpret_cast<char *>(&date),sizeof(date)); arc.write(reinterpret_cast<char *>(&size),sizeof(size)); arc.write(name.c_str(), size+1); // write final '\0' too arc.write(reinterpret_cast<char *>(&bitrate), sizeof(bitrate)); arc.write(reinterpret_cast<char *>(&stereo), sizeof(stereo)); }

The implementation of deserialize() is a bit trickier, since we need to allocate a temporary buffer for the string:

void MP3_clip::deserialize() { ifstream arce("mp3.dat"); int len=0; char *p=0; arc.read(reinterpret_cast <char *> (&date), sizeof(date)); arc.read(reinterpret_cast<char *> (&len), sizeof(len)); p=new char [len+1]; // allocate temp buffer for name arc.read(p, len+1); // copy name to temp, including '\0' name=p; // copy temp to data member delete[] p; arc.read(reinterpret_cast<char *> (&bitrate), sizeof(bitrate)); arc.read(reinterpret_cast<char *> (&stereo), sizeof(stereo)); }

Performance Considerations
You might wonder why we didn't dump the entire object to a file instead of serializing individual data members. In other words, couldn't we implement serialize() as follows:

void MP3_clip::serialize() { ofstream arc("mp3.dat", ios::binary|ios::trunc); arc.write(reinterpret_cast<char *> (this),sizeof(*this)); }

No, we couldn't. There are at least two problems with this approach. Usually, when the serialized object contains other objects, you can't just dump it to a file and reconstitute a valid object subsequently. In our example, the enclosing object contains a "std::string member." A shallow copy would archive transient "std::string" members whose values are transient, meaning that they may change every time you run the program. Worse yet, because "std::string" doesn't actually contain a char array but a pointer, it would be impossible to reconstitute the original string if we used shallow copying. To overcome this problem we didn't serialize the string object. Instead, we archived its characters and length. In general, pointers, arrays and handles should be treated similarly.

Another problem might arise with polymorphic objects. Every polymorphic object contains a vptr—a hidden pointer to a dispatch table that holds the addresses of its virtual functions. The vptr's value is transient. If we dumped an entire polymorphic object to a file and then superimposed the archived data on a new object, that reconstituted object's vptr might be invalid and cause undefined behavior. Here again, the solution is to serialize and deserialize only non-transient data members. Alternatively, you can calculate the exact offset of the vptr and leave it intact when you reconstitute an object from a file. Remember that the vptr's position is implementation-dependent. Therefore, such code isn't portable.

Danny Kalev is a system analyst and software engineer with 13 years of experience, specializing in C++ and object-oriented analysis and design. He is a member of the ANSI C++ standardization committee and the author of ANSI/ISO C++ Professional Programmer's Handbook (Que, 1999, ISBN: 0789720221).
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.