Member can test to see if a given object is found inside a collection. This operation is more subtle than it might appear as subsequent example programs will demonstrate. The key issue is the way two objects are compared to determine if the object being sought matches any of the elements of the collection.
Likewise, After and Before depend on the comparison of objects. Member takes a reference to the type of element the collection is declared to hold--a TCollectibleLong in the example programs. Member returns a pointer to an object of the same type. If the object given to Member as an argument is found, Member returns a pointer to the first matched element of the collection. Otherwise it returns NIL. The return value can be used as a boolean in conditional expressions.
After behaves just like Member, except that it returns a pointer to the element after the found object. Similarly Before returns a pointer to the element appearing in the collection in front of the object given as an argument to Before. Both After and Before return NIL if the object given as an argument is not found, or if there is no element after (for After) or before (for Before) it in the collection. In fact, Member, After, and Before have identical declarations inside TArrayOf.
Member works in much the same way. However, using Member can lead to some interesting results. For example, it's possible that two TCollectibleLongs with identical internal values are not considered to be equal when compared. As an example consider the following program.
The final test, however, might surprise you. The pointer p is set to a new instance of TCollectibleLong with a value of 3.
Depending on what you are trying to do, this definition of equality (defined as sameness) might be too restrictive. You might simply want use Member to find out if a collection contains an instance of TCollectibleLong that has a value of 3. What you are bumping up against here is the mathematical distinction between identity and equality. Identity implies that two objects under consideration are in fact the same object. Equality is less specific in nature. Equality implies that two objects under consideration may be interchanged for a given operation and still produce the same result. Either of two instances of the integer 3 will produce the same result when added to the integer 2. This is true even if both instances of the integer reside at different machine addresses.
Implied in this definition of equality is that the meaning of equality depends on context; the meaning of equality depends on how two items being compared are intended to be used. If you are doing pointer arithmetic based on object references, you may need to define equality and identity as the same thing. If, on the other hand, you are searching a word processor document (a collection or sequence of strings) with a search and replace command, equality of two strings will be true if each of the character elements of two different strings match.
For this reason all MCollectible objects define member functions for determining identity (IsSame) and equality (IsEqual). These member functions are shown in Figure 1
It's useful to look at TCollectibleLong::IsEqual as you might have to override IsEqual if you create a subclass of MCollectible which you plan to use with collections.
Compare this to the code for IsSame (identity) which TCollectibleLong inherits from MCollectible.
The question remains, if TCollectibleLong defines equality based value rather than address, why did the Member function in the previous example program not match the two TCollectibleLongs which both had the value 3? The reason has to do with the way the collection was declared. Recall that the constructor for collections requires two arguments.
Fortunately comparators are templatized in a way that coincides with collections. If the class of elements you are managing in a collection implements the proper behavior for IsEqual, you can easily create a comparator without having to define a new class. A comparator based on IsEqual for TCollectibleLongs can be created as follows.
That's all there is to it. With these two changes, the previous program will now return a true value for the expression
CAUTION
Even though you create a comparator with new, you should not
The entire program follows.
Member
TCollectibleLong *p = new TCollectibleLong(3);
if (collection->Member(*p))
printf("found");
else
printf("not found");
After and Before
Given a pointer to the first element in a collection, template <class AType>
class TArrayOf: public TIndexedSequenceOf<AType>,
public TPrimitiveArray {
public:
...
virtual AType* Member(const AType& obj) const;
virtual AType* After(const AType& obj) const;
virtual AType* Before(const AType& obj) const;
...
}
a pointer to the second element can be obtained using After: TCollectibleLong *pFirst = collection->First();
Note that it is necessary to dereference the pointer pFirst because After expects a reference to a TCollectibleLong even though it returns a pointer. The following example programs shows the use of After to return the element after the first element in a collection and uses Before to return the element before the last element in the same collection. TCollectibleLong *p = collection->After(*pFirst);
Sample program
using After, Before
Here's the resulting output. // Copyright (c) 1994 Taligent, Inc. All Rights Reserved.
#ifndef Taligent_COLLECTIONS
#include <Collections.h>
#endif
#ifndef _H_STDIO
#include <stdio.h>
#endif
int main()
{
TArrayOf<TCollectibleLong>* collection =
new TArrayOf<TCollectibleLong>(NIL, NIL);
collection->Add(new TCollectibleLong(0));
collection->Add(new TCollectibleLong(1));
collection->Add(new TCollectibleLong(2));
collection->Add(new TCollectibleLong(3));
TCollectibleLong *p, *pFirst, *pLast;
pFirst = collection->First();
p = collection->After(*pFirst);
printf("First=>%d\n", pFirst->GetValue());
printf("After First=>%d\n", p->GetValue());
pLast = collection->Last();
p = collection->Before(*pLast);
printf("Before Last=>%d\n", p->GetValue());
printf("Last=>%d\n", pLast->GetValue());
collection->DeleteAll();
delete collection;
return 0;
}
First=>0
After First=>1
Before Last=>2
Last=>3
Member
Sample program
using Member
The resulting output reveals some important and possibly surprising results. After the four TCollectibleLongs are added to collection three tests are made for membership of three different components. A pointer, p is first declared and then set to point to the first member of the collection. // Copyright (c) 1994 Taligent, Inc. All Rights Reserved.
#ifndef Taligent_COLLECTIONS
#include <Collections.h>
#endif
#ifndef _H_STDIO
#include <stdio.h>
#endif
int main()
{
TArrayOf<TCollectibleLong>* collection =
new TArrayOf<TCollectibleLong>(NIL, NIL);
collection->Add(new TCollectibleLong(0));
collection->Add(new TCollectibleLong(1));
collection->Add(new TCollectibleLong(2));
collection->Add(new TCollectibleLong(3));
TCollectibleLong *p;
p = collection->First();
printf("Member First? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
p = new TCollectibleLong(4);
printf("Member 4? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
delete p;
p = new TCollectibleLong(3);
printf("Member 3? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
delete p;
collection->DeleteAll();
delete collection;
return 0;
}
The test for membership is embedded in the following printf function call. TCollectibleLong *p;
p = collection->First();
The subexpression containing the actual test for membership is a tertiary ?: operator which returns the string "yes" if p is a member of collection and "no" if it is not. printf("Member First? =>%s\n",
(collection->Member(*p) ? "yes" : "no"));
Not surprisingly the result of this first test shows that the element returned by collection->First() is a member of collection. (collection->Member(*p) ? "yes" : "no")
In the second test, p is set to a new instance of TCollectibleLong with a value of 4. Member First? =>yes
A a printf call similar to the first test produces the output p = new TCollectibleLong(4);
printf("Member 4? =>%s\n",
(collection->Member(*p) ? "yes" : "no"));
Again, this is no surprise because none of the TCollectibleLongs in collection has a value of 4. Member 4? =>no
The resulting output is p = new TCollectibleLong(3);
printf("Member 3? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
Member 3? =>no
Comparing collection elements for equality
You may have expected Member to return a true value in this case because collection does contain a TCollectibleLong with a value of 3. The problem is the way that comparison is conducted when testing to see if an element is a member of a collection. By default, comparison of elements is based on address pointers. For two objects to compare as equal, they must occupy the same location in memory. Because the second TCollectibleLong(3) was created with a new operation, new heap memory was allocated for it and p points to it. Therefore p does not have the same value as the pointer returned by collection->Last().
IsSame and IsEqual
The IsSame member function is almost always based on pointer comparison as was the operation invoked by Member in the previous program. Most classes, however, will want to override the definition of IsEqual if it makes sense to define equality in a more general way. TCollectibleLong overrides IsEqual to define equality in terms of the values inside of TCollectibleLong objects instead of by the addresses they occupy in memory.
This code defines how to TCollectibleLong objects should be compared for equality. If the pointer to the second object, obj, is not NIL, the code casts obj as a pointer to TCollectibleLong and retrieves its value though TCollectibleLong::GetValue. If the long returned by GetValue is equal to the internal value of the first object, the result is true and the objects are considered equal. bool TCollectibleLong::IsEqual(const MCollectible* obj) const
{
return (obj != NIL)
? (fvalue == ((TCollectibleLong*)obj)->GetValue()) : true;
}
As you can see, identity is based on the idea that two objects are identical if their pointers are equal. bool MCollectible::IsSame(const MCollectible* obj) const
{
return (obj == this);
}
Defining equality
with comparators
The first argument is a comparator and the second argument is a streamer which the collection can use to save itself as a persistent object. So far all the examples have specified NIL for both comparators and streamers used by collections. A NIL comparator tells the collection to base comparison on IsSame for its elements rather than on definition specified by the programmer. If you want to base comparison on IsEqual, you need to create a comparator object and give that object to the constructor.
The resulting pointer can now be given to the constructor for a collection. TMCollectibleComparator<TCollectibleLong> *comparator =
new TMCollectibleComparator<TCollectibleLong>;
even though the two TCollectibleLongs being compared occupy two different locations in memory. collection->Member(new TCollectibleLong(3))
delete it.
Once you pass the comparator off to the collection's constructor, the collection adopts and owns it and will delete it automatically. Be sure you don't delete it yourself or you might get a core dump when you delete the collection. delete comparator. // error: don't do this.
Sample program
using
a comparator
The output shows that Member now returns values that make sense according to the definition of equality selected for this particular job. // Copyright (c) 1994 Taligent, Inc. All Rights Reserved.
#ifndef Taligent_COLLECTIONS
#include <Collections.h>
#endif
#ifndef _H_STDIO
#include <stdio.h>
#endif
int main()
{
TMCollectibleComparator<TCollectibleLong> *comparator =
new TMCollectibleComparator<TCollectibleLong>;
TArrayOf<TCollectibleLong>* collection =
new TArrayOf<TCollectibleLong>(comparator, NIL);
collection->Add(new TCollectibleLong(0));
collection->Add(new TCollectibleLong(1));
collection->Add(new TCollectibleLong(2));
collection->Add(new TCollectibleLong(3));
TCollectibleLong *p;
p = collection->First();
printf("Member First? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
p = new TCollectibleLong(4);
printf("Member 4? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
delete p;
p = new TCollectibleLong(3);
printf("Member 3? =>%s\n", (collection->Member(*p) ? "yes" : "no"));
delete p;
collection->DeleteAll();
delete collection;
return 0;
}
Member First? =>yes
Member 4? =>no
Member 3? =>yes
[Contents]
[Previous]
[Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Generated with WebMaker