Essential Tools Module User's Guide : Chapter 6 Collection Classes : Creating an RWCollectable Object : Add Definitions for Virtual Functions
Add Definitions for Virtual Functions
Class RWCollectable declares the following virtual functions:
 
virtual ~RWCollectable();
virtual RWspace binaryStoreSize() const;
virtual int compareTo(const RWCollectable*) const;
virtual unsigned hash() const;
virtual RWClassID isA() const;
virtual bool isEqual(const RWCollectable*) const;
virtual RWCollectable* newSpecies() const;
virtual void restoreGuts(RWvistream&);
virtual void restoreGuts(RWFile&);
virtual void saveGuts(RWvostream&) const;
virtual void saveGuts(RWFile&) const;
Any class that derives from class RWCollectable should be able to understand any of these methods. Although default definitions are given for all of them in the base class RWCollectable, it is best for you as the class designer to provide definitions tailored to the class at hand.
We've split our discussion of these virtual functions. We discuss the destructor in “Object Destruction”, and the binaryStoreSize(), saveGuts(), and restoreGuts() functions in “How to Add Polymorphic Persistence”, where we describe how to add persistence to a class. Virtual functions isA() and newSpecies() are declared and defined by macros, so they were discussed above, in “Virtual Function isA()” and “Virtual Function newSpecies()”. This section presents discussion on the remaining functions: compareTo(), isEqual(), and hash(). A very brief example, showing how all three functions deal with the same data, appears in “An Example of compareTo(), isEqual(), and hash()”.
Virtual Function compareTo()
The virtual function compareTo() is used to order objects relative to each other. This function is required in collection classes that depend on such ordering, such as RWBinaryTree or RWBTree. Here is its declaration:
 
virtual int compareTo(const RWCollectable*) const;
The function int compareTo(const RWCollectable*) const should return a number greater than zero if self is greater than the argument, a number less than zero if self is less than the argument, and zero if self is equal to the argument.
The definition and meaning of whether one object is greater than, less than, or equal to another object is left to the class designer. The default definition, found in class RWCollectable, is to compare the two addresses of the objects. This default definition should be considered a placeholder; in practice, it is not very useful and could vary from run to run of a program.
Here is a possible definition of compareTo():
 
int Bus::compareTo(const RWCollectable* c) const
{ const Bus* b = (const Bus*)c;
if (busNumber_ == b->busNumber_) return 0;
return busNumber_ > b->busNumber_ ? 1 : -1;
}
Here we are using the bus number as a measure of the ordering of buses. If we need to insert a group of buses into an RWBinaryTree, they would be sorted by their bus number. Note that there are many other possible choices—we could have used the driver name, in which case they would have been sorted by the driver name. Which choice you use will depend on your particular problem.
There is a hazard here. We have been glib in assuming that the actual type of the RWCollectable which c points to is always a Bus. If a careless user inserted, say, an RWCollectableString into the collection, then the results of the cast (const Bus*)c would be invalid, and dereferencing it could bring disaster. “Don't Use Sorted RWCollections to Store Heterogeneous RWCollectable Objects” describes this problem. The necessity for all overloaded virtual functions to share the same signatures requires that they return the lowest common denominator, in this case, class RWCollectable. The result is that much compile-time type checking breaks down.
NOTE >> You must be careful that the members of a collection are either homogeneous (i.e., all of the same type), or that there is some way of telling them apart. The member functions isA() or stringID() can be used for this.
Virtual Function isEqual()
The virtual function isEqual() is a tester function whose purpose is to signal when a certain member of the collection has been identified.
 
bool isEqual(const RWCollectable* c) const;
The function bool isEqual(const RWCollectable*) should return true if the object and its argument are considered equal, and false otherwise. The definition of equality is left to the class designer. The default definition, as defined in class RWCollectable, is to test the two addresses for equality, that is, to test for identity.
Note that isEqual() does not have to be defined as being identical. Rather isEqual can mean that two objects are equivalent in some sense. In fact, the two objects need not even be of the same type. The only requirement is that the object passed as an argument must inherit type RWCollectable. You are responsible for making sure that any typecasts you do are appropriate.
Also note that there is no formal requirement that two objects that compare equal (i.e., compareTo() returns zero) must also return true from isEqual(), although it is hard to imagine a situation where this wouldn't be the case. It is also possible to design a class for which the isEqual test returns true for objects that have different hash values. This would make it impossible to search for such objects in a hash-based collection.
For the Bus class, an appropriate definition of isEqual might be:
 
bool Bus::isEqual(const RWCollectable* c) const {
const Bus* b = (const Bus*)c;
return busNumber_ == b->busNumber_;
}
Here we are considering buses to be equal if their bus numbers are the same. Again, other choices are possible.
Virtual Function hash()
The function hash() should return an appropriate hashing value for the object. Here is the function's declaration:
 
unsigned hash() const;
A possible definition of hash() for our class Bus might be:
 
unsigned Bus::hash() const{
return (unsigned)busNumber_;
}
The example above simply returns the bus number as a hash value. Alternatively, we could choose the driver's name as a hash value:
 
unsigned Bus::hash() const{
return driver_.hash();
}
In the above example, driver_ is an RWCString that already has a hash function defined.
NOTE >> We expect that two objects that test true for isEqual will hash to the same value.
An Example of compareTo(), isEqual(), and hash()
We have described three inherited virtual functions: compareTo(), isEqual(), and hash(). Here is an example that defines a set of objects, and applies the functions. The results of the functions appear as comments in the code.
 
RWCollectableString a("a");
RWCollectableString b("b");
RWCollectableString a2("a");
a.compareTo(&b); // Returns -1
a.compareTo(&a2); // Returns 0 ("compares equal")
b.compareTo(&a); // Returns 1
a.isEqual(&a2); // Returns true
a.isEqual(&b); // Returns false
a.hash() // Returns 96 (operating system dependent)
Note that the compareTo() function for RWCollectableStrings has been defined to compare strings lexicographically in a case sensitive manner. See class RWCString in the SourcePro API Reference Guide for details.