Create a persistent object in C ++

zhaozj2021-02-16  46

Create a persistent object in C

Danny Kalev

Persistent Objects is widely used in games, distributed database systems, multimedia, and graphics applications. At present, C does not directly support persistence (but there are some suggestions to add persistence and reflection in C future versions). Persistent objects maintain its own state outside the scope of the program. Write the object to a file and rebuilt it later, or transmitting the object to a remote machine, which is such an example. Support for persistence is not as simple as the first eye, the size of the same object and the memory layout may not be the same on different platforms, and different byte order (byte ordering), or Endian-Ness Make things more complicated. Here I will discuss how to achieve persistence without paying for a third-party framework such as DCOM and CORBA. This is a valid and satisfactory solution for small and portable applications.

Serialization foundation

To make an object last forever, it must be saved in a non-volatile storage device. Consider an application that recorded and playing the MP3 file, each one is expressed as an object containing the title, the record, singer, time, rate, recording date, and the corresponding MP3 file, which displays the recent play in the trace list Tape. Your goal is to be serialized, that is, write objects to a file, making MP3 objects into persistent objects, and reconstructing these objects in the next session through the DeSerialization.

Serialized built-in data type

Each object is ultimately consisting of built-in data members such as int, bool, char [], and more. Your first task is to write such types into an output file stream. The application must be stored as the corresponding binary form, based on this, Write () and read () member functions should be used. Write () writes the bit mode of the variable into a file stream with the address and size of a certain variable. The two parameters of the READ () are the char * and long types, indicating the address and byte size of the memory buffer. The following example demonstrates how to save two integers in OFSTream:

#include

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 (& x), sizeof (x));

Archive.write (ReinterPret_cast (& x), sizeof (x));

Archive.close ();

}

It is necessary to use ReinterPret_Cast <> because Write ()'s first parameter type is const char *, but & x and & y are int * type.

The following code reads the value just stored:

#include

Using namespace std;

int main ()

{

INT X, Y;

IFStream Archive ("Coord.dat");

Archive.read ((ReinterPret_cast (& x), sizeof (x)); archive.read (Reinterpret_cast , sizeof (y));

}

Serialized object

To serialize a complete object, write each data member into 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 name's length

// EMPTY FILE IT ALREADY EXISTS BEFORE WRITING DATA

OFSTREAM ARC ("MP3.DAT", iOS :: binary | ios :: trunc);

Arc.write (Reinterpret_cast (& date), sizeof (date));

Arc.write (ReinterPret_cast ), sizeof (size);

Arc.write (Name.c_STR (), Size 1); // Write Final '/ 0' TOO

Arc.write (Reinterpret_cast ,

SIZEOF (Bitrate));

Arc.write (Reinterpret_cast (& Stereo),

SIZEOF (Stereo);

}

Realizing Deserialize () requires some tips, because you need to assign a temporary buffer for a string. The practice is as follows:

Void MP3_Clip :: Deserialize ()

{

IFStream Arce ("mp3.dat");

INT LEN = 0;

CHAR * P = 0;

Arc.read (Reinterpret_cast ), sizeof (date));

Arc.read (ReinterPret_cast ); 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 ,

SIZEOF (Bitrate));

Arc.read (ReinterPret_cast (& stereo),

SIZEOF (Stereo);

}

Performance optimization

You may be confused, why not dump the entire object to a file, but must serialize each data member? In other words, is it possible to implement serialize () in the following way?

Void mp3_clip :: serialize ()

{

OFSTREAM ARC ("MP3.DAT", iOS :: binary | ios :: trunc);

Arc.write (ReinterPret_cast (this), sizeof (* this));

No, you can't do this. There are at least two problems in this way. Typically, when serialized objects also contain other objects, you cannot simply dump the object into a file and expect to rebuild a valid object from it later. In our example, the outer object contains a std :: String member, a shallow copy (ShaLlow Copy) to file the std :: string member, but its value is time change, meaning when running the program at a time It may change. Worse, because std :: string does not contain a character array, but a pointer, use a shallow copy to try to rebuild the original string is impossible. To overcome this problem, the program has no serialization String object, but the character and length of its contained. In general, pointers, arrays and handles should be processed in the same way.

Another problem is designed to polymorphism. Each polymorphic object contains a VTPR, which is a hidden pointer to the virtual function address allocation table. The value of the VTPR is time-variable, if you dump the entire polymorphism object to a file, then forcibly add the files after the files, the VPTR may be invalid and cause undefined behavior. Remind again that the solution is to serialize and reverse sequencing only non-time-transition data members. Another method is to calculate the exact offset of the VPTR, and do not move it when rebuilding objects from the file. Remember, the location of the VPTR is related to implementation, so such code is unmistable.

summary

Although C does not directly support object persistence, it is not difficult to manually implement it, as long as you follow some basic guidelines: first break each composite object into raw data type, then serialize these raw data types. When serialization data, remember to run the value to be run. During the reverse sequence, read the value just stored. Handling String objects, arrays and handles require some techniques: always declare them, and store them pointed to. Remember to store the String or array size in a separate field.

转载请注明原文地址:https://www.9cbs.com/read-26334.html

New Post(0)