### Using Element Pointers

If you create a collection of C++ pointers or pointers of type IMngPointer or IAutoPointer, Collection Classes methods that use element comparison functions will do the comparison on the elements' pointers instead of on the elements themselves.

#### Directing Element Functions to the Referenced Element

The classes IElemPointer, IMngElemPointer, and IAutoElemPointer internally use a function called elementForOps to direct functions such as equal and compare to the referenced element, so that they are not applied to the pointer itself and so that instantiations such as ISet <IElemPointer <Person>> perform the functions on the elements. This indirection is usually transparent but you must consider it when you derive classes from the IElemPointer class. The standard operation classes first apply a function elementForOps to the element before they apply the corresponding non-member (equal, ...) function. By default, a corresponding template function is instantiated for elementForOps which takes an element as input and returns that element. For pointer classes that perform operations on the pointers themselves (IAutoPointer, IMngPointer), this function takes the pointer as input and returns the same pointer. For pointer classes that perform the operations on the referenced elements (IElemPointer, IAutoElemPointer, IMngElemPointer), this function takes the pointer as input and returns the referenced element. If a class derived from IElemPointer<E> is used as a collection element type, the default template functions must be instantiated before a conversion will be considered. A derived class must therefore explicitly redefine the elementForOps function, as shown in the following example, where class PersonPtr redefines both versions of elementForOps by calling the default elementForOps with a PersonPtr as argument. Both versions are then made to return a cast to Person reference.

    //person.h - header file containing class Person
    #include <iostream.h>
    #include <istring.hpp>
    #include <iptr.h>

    class Person
    {
        IString PersonName; //This will be used as the key
        IString TNumber;

    public:
        //constructor
        Person () : PersonName(""), TNumber("") {}

        //copy constructor
        Person(IString Name, IString Number)
            : PersonName(Name), TNumber(Number)
        {
        }

        IString const& GetPersonName() const { return PersonName; }
        IString const& GetTNumber() const { return TNumber; }
        IBoolean operator==(Person const& A) const
        {
            return (PersonName == A.GetPersonName()) &&
                (TNumber==A.GetTNumber());
        }

        IBoolean operator<(Person const& A) const
        {
            return (PersonName < A.GetPersonName());
        }
    };

    class PersonPtr : public IElemPointer<Person> {

        friend inline Person& elementForOps (PersonPtr& A)
        {
            return (Person&)elementForOps((IElemPointer<Person>&)A);
        }

        friend inline Person const& elementForOps(PersonPtr const& A)
        {
            return (Person const&)elementForOps((IElemPointer<Person>&)A);
        }

    public:
        PersonPtr() : IElemPointer<Person>() {}
        PersonPtr(Person* ptr,IExplicitInit IINIT)
            : IElemPointer<Person>(ptr,IINIT) {}
    };

    ostream& operator<<(ostream& os,Person A);

    inline IString const& key(Person const& A) //Key access
    {
        return A.GetPersonName();
    };

This is the main file.

    //main.cpp - main file
    #include "person.h"   //person.h from the previous example
    #include <istdops.h>
    #include <iset.h>

    typedef ISet <PersonPtr> AddressList;

    ostream& operator<<(ostream& os,Person A)
    {
        return (os << endl << A.GetPersonName() << " " <<
            A.GetTNumber());
    }

    void main()
    {
        AddressList Business;
        AddressList::Cursor myCursor1(Business);

        PersonPtr Aptr (new Person("Peter Black","714-50706"),IINIT);
        PersonPtr Bptr (new Person("Carl Render","714-540321"),IINIT);
        PersonPtr Cptr (new Person("Sandra Summers","x"),IINIT);
        PersonPtr Dptr (new Person("Mike Summers","x"),IINIT);
        PersonPtr CopyCptr (new Person("Sandra Summers","x"),IINIT);

        Business.add(Aptr);
        Business.add(Bptr);
        Business.add(Cptr);
        Business.add(Dptr);
        Business.add(CopyCptr);

        forICursor (myCursor1) {
            cout << *Business.elementAt(myCursor1);
        }
    }

CopyCptr and Cptr refer to different memory addresses, so both of them could be put into the set. Using element pointers rather than regular pointers, all collection functions are done on the elements to which the pointers point. That is why a pointer pointing on Sandra Summers is only entered once into the list.

#### Element Functions That Work on the Pointers Instead of the Referenced Elements

If you do want element functions to work on the pointers instead of the referenced elements, you do not need to implement equality and ordering relation for the chosen pointer type (IAutoPointer, IMngPointer or C++ pointers). The compiler can instantiate the default element function templates in such cases. If necessary, you can implement your element functions for the referenced element type.

In the following example, adding, locating, and other functions are based on pointer equality and ordering, and not on the equality defined for the Person type.

    //main.cpp - main file
    #include "person.h" //person.h from the previous example
    #include <istdops.h>
    #include <iset.h>

    typedef IMngPointer <Person> ManagedPersonPtr;
    typedef ISet <ManagedPersonPtr> AddressList;

    ostream& operator<<(ostream& os,Person A)
    {
        return (os << endl << A.GetPersonName()
            << " " << A.GetTNumber());
    }

    void main()
    {
        AddressList Business;
        AddressList::Cursor myCursor1(Business);

        ManagedPersonPtr ptrA(new Person("Peter Black",
            "714-50706"), IINIT);

        ManagedPersonPtr ptrB(new Person("Carl Render",
            "714-540321"), IINIT);

        ManagedPersonPtr ptrC(new Person("Sandra Summers",
            "x"), IINIT);

        ManagedPersonPtr ptrD(new Person("Mike Summers",
            "x"), IINIT);

        ManagedPersonPtr copyPtrC(new Person("Sandra Summers",
            "x"), IINIT);

        Business.add(ptrA);
        Business.add(ptrB);
        Business.add(ptrC);
        Business.add(ptrD);
        Business.add(copyPtrC);

        forICursor (myCursor1) {
            cout << *Business.elementAt(myCursor1);
        }
    }

The variables copyPtrC and ptrC refer to different memory addresses, so both of them are entered into the set even if the element they point to is identical. This is because equality now refers to the pointers even though it is also defined for Person.

Introduction to the Collection Classes
Collection Class Hierarchy
Overall Implementation Structure
Adding Elements
Removing Elements
Replacing Elements
Smart Pointers

Implementing Element- and Key-Type Functionality
Choosing the Appropriate Smart Pointer Class
Constructing Smart Pointers
Using Automatic Pointers
Using Managed Pointers
Things to Watch Out For When Using Smart Pointers
Copying and Referencing Collections
Instantiating the Collection Classes