Rogue Wave banner
Previous fileTop of documentContentsIndexNext file

14.3 Simple Persistence

Simple persistence is the storage and retrieval of an object to and from a stream. Table 1 lists the classes in Tools.h++ that use simple persistence.

Table 1. Classes with Simple Persistence

Category
Description
C++ fundamental types
int, char, float, ...
Rogue Wave date and time classes
RWDate, RWTime
Rogue Wave string classes
RWCString, RWWString
Miscellaneous Rogue Wave classes
RWBitVec

Because it is straightforward, simple persistence is a quick and easy way to save and restore objects that have neither pointers to other objects nor virtual member functions.

However, when objects that refer to each other are saved and then restored with simple persistence, the pointer relationships, or morphology, among the objects can change. This is because simple persistence assumes that every pointer reference to an object in memory refers to a unique object. Thus, when an object is saved with simple persistence, two references to the same memory location will cause two copies of the contents of that memory location to be saved. Not only does this use extra space in the stream, but it also causes the restored object to point to two distinct copies of the referenced object.

14.3.1 Two Examples of Simple Persistence

Let's look at a two examples of simple persistence. The first example illustrates successful persistence of fundamental datatypes, and demonstrates the Tools.h++ overloaded operators operator<< and operator>>, which save and restore persistent objects. The second example illustrates one of the problems with simple persistence -- its inability to maintain pointer relationships among objects.

14.3.1.1 Example One: Simple Persisting Objects of Fundamental Type

This example uses simple persistence to save two integers to an output stream po, which saves the integers to the file int.dat. Then the example restores the two integers from the stream pi, which reads the integers from the file int.dat.

The example uses the overloaded insertion operator operator<< to save the objects, and the overloaded extraction operator operator>> to restore the objects, much the same way as you use these operators to output and input objects in C++ streams.

Note that the saving stream and the restoring stream are put into separate blocks. This is so that opening pi will cause it to be positioned at the beginning of the file.

Here's the code:

#include <assert.h>
#include <fstream.h>
#include <rw/pstream.h>

main (){
  int j1 = 1;
  int k1 = 2;

  // Save integers to the file "int.dat"
    {
    // Open the stream to save to:
        ofstream          f("int.dat");
        RWpostream        po(f);

    // Use overloaded insertion operator 
    // "RWpostream::operator<<(int)" to save integers:
        po << j1;
        po << k1;
    }

  // Restore integers from the file "int.dat"
    int j2 = 0;
    int k2 = 0;
    {
    // Open a separate stream to restore from:       
        ifstream          f("int.dat");
        RWpistream        pi(f);

    // Use overloaded extraction operator
    // "RWpistream::operator>>(int)" to restore integers:
        pi >> j2;         // j1 == j2
        pi >> k2;         // k1 == k2
    }

  assert(j1 == j2);
  assert(k1 == k2);
  return 0;
}

The preceding example shows how easy it is to use overloaded operators to implement this level of persistence. So, what are some of the problems with using simple persistence? As mentioned above, one problem is that simple persistence will not maintain the pointer relationships among objects. We'll take a look at this problem in the next example.

14.3.1.2 Example Two: Simple Persistence and Pointers

This example shows one of the shortcomings of simple persistence: its inability to maintain the pointer relationships among persisted objects. Let's say that you have a class Developer that contains a pointer to other Developer objects:

Developer {
  public:
    Developer(const char* name, const Developer* anAlias = 0L)
      : name_(name), alias_(anAlias) {}

  RWCString        name_;  // Name of developer.
  const Developer* alias_;  // Alias points to another Developer.
};

Now let's say that you have another class, Team, that is an array of pointers to Developers:

class Team {
  public:
    Developer*  member_[3];
};

Note that Team::member_ doesn't actually contain Developers, but only pointers to Developers.

We'll assume that you've written overloaded extraction and insertion operators that use simple persistence to save and restore Developers and Teams. The example code for this is omitted to keep the explanation from getting cluttered.

When you save and restore a Team with simple persistence, what you restore may be different from what you saved. Let's look at the following code, which creates a team, then saves and restores it with simple persistence.

main (){
  Developer*  kevin   = new Developer("Kevin");
  Developer*  rudi    = new Developer("Rudi", kevin);
  Team        team1;

  team1.member_[0] = rudi;
  team1.member_[1] = rudi;
  team1.member_[2] = kevin;

  // Save with simple persistence:
  {
    RWFile    f("team.dat");
    f << team1;  // Simple persistence of team1.
  }

  // Restore with simple persistence:
  Team        team2;
  {
    RWFile            f("team.dat");
    f >> team2;
  }
  return 0;
}

Because this example uses simple persistence, which does not maintain pointer relationships, the restored team has different pointer relationships than the original team. Figure 1 shows what the created and restored teams look like in memory if you run the program.

Figure 1. Simple Persistence

image4.gif


Collection to be saved (team1).

image5.gif


Collection restored (team2).

As you can see in Figure 1, when objects that refer to each other are saved and then are restored with simple persistence, the morphology among the objects can change. This is because simple persistence assumes that every pointer reference to an object in memory refers to a unique object. Thus, when such objects are saved, two references to the same memory location will cause two copies of the contents of that memory location to be saved, and later restored.


Previous fileTop of documentContentsIndexNext file
©Copyright 1999, Rogue Wave Software, Inc.
Send mail to report errors or comment on the documentation.