Create a Persistent Object in C ++

Source: Internet
Author: User

Persistent objects is widely used in games, distributed database systems, multimedia and graphics applications. Currently, C ++ does not directly support persistence (but there are some suggestions for adding persistence and reflection (reflection) in future C ++ versions ). A persistent object can maintain its own State outside the scope of the program it was created. This is an example of writing an object into a file and recreating it later, or transferring the object to a remote machine. The support for persistence is not as simple as the first glance. The size and memory of the same object may not be the same on different platforms, but different bytes of order (byte ordering ), or endian-ness, which makes things more complicated. In the following sections, I will discuss how to implement persistence without turning to third-party frameworks such as DCOM and CORBA. This is an effective and satisfactory solution for small and portable applications.

 

Serialization Basics

To make an object persistent, you must save its status in a non-easy-to-lose storage device. Consider an application for recording and playing MP3 files. Each single is represented as an object containing titles, records, singers, times, rates, recording dates, and corresponding MP3 files, this application displays recently played tracks in the trail list. Your goal is to write an object into a file through serialization, so that the MP3 object becomes a persistent object, and re-create these objects in the next session through deserialization.

 

Serialize built-in data types

Each object is eventually composed of built-in data members, such as int, bool, and char. Your first task is to write such a type into an output file stream (ofstream. Applications must store these values in binary format. For this purpose, the write () and read () member functions should be used. Write () writes the bit mode of a variable into a file stream based on the address and size of a variable. The two parameters of read () are char * and long, respectively indicating the address and byte size of the memory buffer. The following example shows how to save two integers in ofstream:

 

# Include <fstream>

Using namespace std;

Int main ()

{

Int x, y; // mouse coordinates

// .. Assign values to x and y

Ofstream archive ("coord. dat", ios: binary );

Archive. write (reinterpret_cast <char *> (& x), sizeof (x ));

Archive. write (reinterpret_cast <char *> (& x), sizeof (x ));

Archive. close ();

}

 

It is necessary to use reinterpret_cast <> because the first parameter type of write () is const char *, but & x and & y are int.

 

The following code reads the stored value:

 

# Include <fstream>

Using namespace std;

Int main ()

{

Int x, y;

Ifstream archive ("coord. dat ");

Archive. read (reinterpret_cast <char *> (& x), sizeof (x ));

Archive. read (reinterpret_cast <char *> (& y), sizeof (y ));

}

 

Serialized object

To serialize a complete object, each data member should be written to the 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 names 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 too

Arc. write (reinterpret_cast <char *> (& bitrate ),

Sizeof (bitrate ));

Arc. write (reinterpret_cast <char *> (& stereo ),

Sizeof (stereo ));

}

 

Some tips are required to implement deserialize (), because you need to assign a temporary buffer to the string. The procedure is as follows:

 

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

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 Optimization

You may be confused. Why do not I need to serialize each data member instead of dumping the entire object to a file at a time? In other words, can't we use the following method to implement serialize?

 

Void MP3_clip: serialize ()

{

Ofstream arc ("mp3.dat", ios: binary | ios: trunc );

Arc. write (reinterpret_cast <char *> (this), sizeof (* this ));

}

 

No. You cannot do this. This method has at least two problems. Generally, when a serialized object contains other objects, you cannot simply dump the object to a file and expect to recreate a valid object from it. In our example, the outer object contains a std: string member. A shallow copy operation will archive the std: string member, but its value is time-varying, it means that the program may change every time it runs. Worse, since std: string does not actually contain an array of characters but a pointer, it is impossible to use a shortest copy to try to recreate the original string. To overcome this problem, the program does not serialize the string object, but archives its characters and lengths. Generally, pointers, arrays, and handles should be processed in the same way.

 

Another problem is to design a multi-state object. Each polymorphism object contains a vtpr, which is a hidden pointer to the virtual function Address Allocation Table. The value of vtpr is time-varying. if you dump the entire polymorphism object to a file and forcibly Add the archived data to a new object, the vptr may be invalid and lead to undefined behavior. Again, the solution is to serialize and deserialize only non-time-varying data members. Another method is to calculate the exact offset of the vptr. Do not change it when recreating an object from a file. Remember, the location of the vptr is related to the implementation, so such Code cannot be transplanted.

 

Summary

Although C ++ does not directly support object persistence, it is not difficult to implement it manually. As long as you follow some basic principles: first, divide each composite object into the original data type, then serialize the original data types. When serializing data, remember to skip time-varying values. In the deserialization process, read the stored value. To process string objects, arrays, and handles, you must always reference them and store the values they point. Remember to store the size of a string or array in a separate field.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.