Essential Tools Module User's Guide : Chapter 6 Collection Classes : Smalltalk‑like Collection Classes : Virtual Functions Inherited From RWCollection
Virtual Functions Inherited From RWCollection
The Smalltalk-like collection classes inherit from the abstract base class RWCollection, which in turn inherits from the abstract base class RWCollectable, described in “Designing an RWCollectable Class”, “Creating an RWCollectable Object”, “Complete Listing for Class Bus”, and “Tables of the Smalltalk-like Classes”. (Thus do we produce collections of collections, but that is another story.)
An abstract base class is a class intended to be inherited by some other class, not used as itself per se. If you think of it as a kind of virtual class, you can easily project the meaning of virtual functions. These virtual functions provide a blueprint of functionality for the derived class. As an abstract base class, RWCollection provides a blueprint for collection classes by declaring various virtual functions, such as insert(), remove(), entries(), and so on.
This section describes the virtual functions inherited by the Smalltalk-like collections. Any of these collections can be expected to understand them.
insert()
You can put a pointer to an object into a collection by using the virtual function insert():
 
virtual RWCollectable* insert(RWCollectable*);
This function inserts in the way most natural for the collection. Faced with a stack, it pushes an item onto the stack. Faced with a queue, it appends the item to the queue. In a sorted collection, it inserts the new item so that items before it compare less than itself, items after it compare greater than itself, and items equal compare equal, if duplicates are allowed. See the example in “Smalltalk-like Classes Example” for an example using insert().
You must always insert pointers to real objects. Since all RWCollection classes need to dereference their contents for some methods such as find(), inserting a zero will cause such methods to crash. If you must store an empty object, we suggest you create and insert a default constructed object of the appropriate type, such as RWCollectable*.
find() and Friends
You can use the following virtual functions to test how many objects a collection contains, and whether it contains a particular object:
 
virtual bool contains(const RWCollectable*) const;
virtual unsigned entries() const;
virtual RWCollectable* find(const RWCollectable*) const;
virtual bool isEmpty() const;
virtual unsigned occurrencesOf(const RWCollectable*) const;
The function isEmpty() returns true if the collection contains no objects. The function entries() returns the total number of objects that the collection contains.
The function contains() returns true if the argument is equal to an item within the collection. The meaning of is equal to depends on the collection and the type of object being tested. Hashing collections use the virtual function isEqual() to test for equality, after first hashing the argument to reduce the number of possible candidates to those in one hash bucket. (Here it is important that all items which are isEqual with each other hash to the same value!). Sorted collections search for an item that compares equal to the argument; in other words, an item for which compareTo() returns zero.
The virtual function occurrencesOf() is similar to contains(), but returns the number of items that are equal to the argument.
The virtual function find() returns a pointer to an item that is equal to its argument.
The following example, which builds on the example in “Smalltalk-like Classes Example”, uses find() to find occurrences of Mary in the collection, and occurrencesOf() to find the number of times Mary occurs:
 
#define RW_STD_TYPEDEFS 1
#include <rw/bintree.h> //1
#include <rw/collstr.h>
#include <rw/rstream.h>
using std::cout;
using std::endl;
int main(){
// Construct an empty SortedCollection
SortedCollection sc;
// Insert some RWCollectableStrings:
sc.insert(new RWCollectableString("George"));
sc.insert(new RWCollectableString("Mary"));
sc.insert(new RWCollectableString("Bill"));
sc.insert(new RWCollectableString("Throkmorton"));
sc.insert(new RWCollectableString("Mary")); //2
cout << sc.entries() << endl; //3
RWCollectableString dummy("Mary"); //4
RWCollectable* t = sc.find( &dummy ); //5
if(t){ //6
if(t->isA() == dummy.isA()) //7
cout << *(RWCollectableString*)t << endl; //8
}
else
cout << "Object not found.\n"; //9
cout << sc.occurrencesOf(&dummy) << endl; //10
sc.clearAndDestroy();
return 0;
}
Program Output:
 
5
Mary
2
//1 This initial code block is duplicated from “Smalltalk-like Classes Example”.
//2 Insert another instance with the value Mary.
//3 Prints out 5, the total number of entries in the sorted collection.
//4 Constructs a throwaway variable dummy to be used to test for the occurrences of strings containing Mary.
//5 The collection is asked to return a pointer to the first object encountered that compares equal to the argument. A nil pointer (zero) is returned if there is no such object.
//6 The pointer is tested to make sure it is not nil.
//7 Paranoid check. In this example, it is obvious that the items in the collection must be of type RWCollectableString. In general, it may not be obvious.
//8 Because of the results of Step 7, the cast to an RWCollectableString pointer is safe. The pointer is then dereferenced and printed.
//9 If the pointer t was nil, then an error message would have been printed here.
//10 The call to occurrencesOf() returns the number of items that compare equal to its argument. In this case, two items are found, the two occurrences of Mary.
remove() Functions
To search for and remove particular items, you can use the functions remove() and removeAndDestroy():
 
virtual RWCollectable* remove(const RWCollectable*);
virtual void removeAndDestroy(const RWCollectable*);
The function remove() looks for an item that is equal to its argument and removes it from the collection, returning a pointer to it. It returns nil if no item is found.
The function removeAndDestroy() is similar except it deletes the item instead of returning it, using the virtual destructor inherited by all RWCollectable items. You must be careful when using this function that the item was actually allocated off the heap, not the stack, and that it is not shared with another collection.
The following example, which expands on the previous one, demonstrates the use of the virtual function removeAndDestroy():
 
RWCollectable* oust = sc.remove(&dummy); //11
delete oust; //12
sc.removeAndDestroy(&dummy); //13
//11 Removes the first occurrence of the string containing Mary and returns a pointer to it. This pointer will be nil if there is no such item.
//12 Deletes the item, which was originally allocated off the heap. There is no need to check the pointer against nil because the language guarantees that it is always OK to delete a nil pointer.
//13 In this statement, the remaining occurrence of Mary is both removed and deleted.
apply() Functions
To efficiently examine the members of a Smalltalk-like collection, use the member function apply():
 
virtual void apply(RWapplyCollectable ap, void* x);
The first argument, RWapplyCollectable, is a typedef:
 
typedef void (*RWapplyCollectable)(RWCollectable*, void*);
In other words, RWapplyCollectable is a pointer to a function with prototype:
 
void yourApplyFunction(RWCollectable* item, void* x)
where yourApplyFunction is the name of the function. You must supply this function. It will be called for each item in the collection, in whatever order is appropriate for the collection, and passed as a pointer to the item as its first argument. You must be careful that you cast the pointer item to the proper derived class. The second argument x is passed through from the call to apply(), and is available for your use. For example, you could use it to hold a handle to a window on which the object is to be drawn.
The apply-functions generally employ the most efficient method for examining all members of the collection. This is their great advantage. Their disadvantage is that they are slightly clumsy to use, requiring you to supply a separate function. The functional equivalent to apply() in the Smalltalk world is do. It takes just one argument: a piece of code to be evaluated for each item in the collection.
Functions clear() and clearAndDestroy()
To remove all items from the collection, you can use the functions clear() and clearAndDestroy():
 
virtual voidclear();
virtual voidclearAndDestroy();
The function clearAndDestroy() not only removes the items, but also calls the virtual destructor for each item. You must use this function with care. The function does check to see if the same item occurs more than once in a collection (by building an RWIdentitySet internally), and thereby deletes each item only once. However, it cannot check whether an item is shared between two different collections. You must also be certain that every member of the collection was allocated off the heap.