17.2 Templates for Data Abstraction

Equal wealth and equal opportunities of culture . . . have simply made us all members of one class.

EDWARD BELLAMY, Looking Backward: 2000–1887

As you saw in the previous section, function definitions can be made more general by using templates. In this section, you will see that templates can also make class definitions more general.

Syntax for Class Templates

The syntax for class templates is basically the same as that for function templates. The following is placed before the template definition:

template<class T>

The type parameter T is used in the class definition just like any other type. As with function templates, the type parameter T represents a type that can be any type at all; the type parameter does not have to be replaced with a class type. As with function templates, you may use any (nonkeyword) identifier instead of T.

For example, the following is a class template. An object of this class contains a pair of values of type T; if T is int, the object values are pairs of integers, if T is char, the object values are pairs of characters, and so on.

//Class for a pair of values of type T: template<class T> class Pair
{ public:
    Pair();
    Pair(T firstValue, T secondValue);
    void setElement(int position, T value);
    //Precondition: position is 1 or 2.
    //Postcondition:
    //The position indicated has been set to value.
    T getElement(int position) const;
    //Precondition: position is 1 or 2.
    //Returns the value in the position indicated.
private:
    T first;
    T second;
};

Once the class template is defined, you can declare objects of this class. The declaration must specify what type is to be filled in for T. For example, the following code declares the object score so it can record a pair of integers and declares the object seats so it can record a pair of characters:

Pair<int> score;
Pair<char> seats;

The objects are then used just like any other objects. For example, the following sets the score to be 3 for the first team and 0 for the second team:

score.setElement(1, 3);
score.setElement(2, 0);

The member functions for a class template are defined the same way as member functions for ordinary classes. The only difference is that the member function definitions are themselves templates. For example, the following are appropriate definitions for the member function setElement and for the constructor with two arguments:

//Uses iostream and cstdlib:
 template<class T>
 void Pair<T>::setElement(int position, T value)
{
    if (position == 1)
        first = value;     else if (position == 2)
        second = value;     else
    {
        cout << "Error: Illegal pair position.\n";
        exit(1);
    }
}
template<class T>
Pair<T>::Pair(T firstValue, T secondValue)
       : first(firstValue), second(secondValue)
{
     //Body intentionally empty.
}

Notice that the class name before the scope resolution operator is Pair<T>, not simply Pair.

The name of a class template may be used as the type for a function parameter. For example, the following is a possible declaration for a function with a parameter for a pair of integers:

int addUp(const Pair<int>& thePair);
//Returns the sum of the two integers in thePair.

Note that we specified the type, in this case int, that is to be filled in for the type parameter T.

You can even use a class template within a function template. For example, rather than defining the specialized function addUp given above, you could instead define a function template as follows so that the function applies to all kinds of numbers:

template<class T>
T addUp(const Pair<T>& thePair);
//Precondition: The operator + is defined for values of type T.
//Returns the sum of the two values in thePair.

Programming Example An Array Class

Display 17.4 contains the interface for a class template whose objects are lists. Since this class definition is a class template, the lists can be lists of items of any type whatsoever. You can have objects that are lists of values of type int, or lists of values of type double, or lists of objects of type string, or lists of items of any other type.

Display 17.4 Interface for the Class Template GenericList

 1    //This is the header file genericlist.h. This is the interface for the
 2    //class GenericList. Objects of type GenericList can be a list of items
 3    //of any type for which the operators << and = are defined.
 4    //All the items on any one list must be of the same type. A list that
 5    //can hold up to max items all of type Type_Name is declared as follows:
 6    //GenericList<Type_Name> the_object(max);
 7    #ifndef GENERICLIST_H
 8    #define GENERICLIST_H
 9    #include <iostream>
10    using namespace std;
11    
12    namespace listsavitch
13    {
14        template<class ItemType>
15        class GenericList
16        {
17        public:
18            GenericList(int max);
19            //Initializes the object to an empty list that can hold up to
20            //max items of type ItemType.
21            ~GenericList( );
22             //Returns all the dynamic memory used by the object to the freestore.
23            
24            int length( ) const;
25            //Returns the number of items on the list.
26            
27            void add(ItemType newItem);
28            //Precondition: The list is not full.
29            //Postcondition: The newItem has been added to the list.
30            
31            bool full( ) const;
32            //Returns true if the list is full.
33            
34            void erase( );
35            //Removes all items from the list so that the list is empty.
36            
37            friend ostream& operator <<(ostream& outs,
38                                       const GenericList<ItemType>& theList)
39            {
40                   for (int i = 0; i < theList.currentLength; i++)
41                          outs << theList.item[i] << endl;
42                   return outs;
43            }
44            //Overloads the << operator so it can be used to output the
45            //contents of the list. The items are output one per line.
46            //Precondition: If outs is a file output stream, then outs has
47            //already been connected to a file.
48            //
49            //Note the implementation of the overloaded << in the header
50            //file! This is commonly done with overloaded friend templates.
51            //Since << is a friend it is NOT a member of the class but
52            //rather in the namespace, this is the simplest implementation
53            //and may make more sense than putting it in genericlist.cpp.
54        private:
55            ItemType *item; //pointer to the dynamic array that holds the list.
56            int maxLength; //max number of items allowed on the list.
57            int currentLength; //number of items currently on the list.
58        };
59    }//listsavitch
60    #endif //GENERICLIST_H

Display 17.5 contains a demonstration program that uses this class template. Although this program does not really do anything much, it does illustrate how the class template is used. Once you understand the syntax details, you can use the class template in any program that needs a list of values. Display 17.6 gives the implementation of the class template.

Display 17.5 Program Using the GenericList Class Template

Output


Output
firstList =
1
2
secondList =
A
B
C

Notice that we have overloaded the insertion operator << so it can be used to output an object of the class template GenericList. To do this, we made the operator << a friend of the class. In order to have a parameter that is of the same type as the class, we used the expression GenericList<ItemType> for the parameter type. When the type parameter is replaced by, for example, the type int, this list parameter will be of type GenericList<int>.

Also note that that the implementation of the overloaded insertion operator << has been placed in the header file rather than the implementation file. This may seem unusual, but it is quite common when using friend functions or operators within a template. Although we are defining << like it is a member of GenericList, recall that friend functions really exist outside the class and are part of the namespace. The compiler will have an easy time finding the implementation of << this way when the class is included from other files.

A note is in order about compiling the code from Displays 17.4, 17.5, and 17.6. A safe solution to the compilation of this code is to #include the template class definition and the template function definitions before use, as we did. In that case, only the file in Display 17.5 needs to be compiled. Be sure that you use the #ifndef #define #endif mechanism to prevent multiple file inclusion of all the files you are going to #include.

Display 17.6 Implementation of GenericList

 1    //This is the implementation file: genericlist.cpp
 2    //This is the implementation of the class template named GenericList.
 3    //The interface for the class template GenericList is in the
 4    //header file genericlist.h.
 5    #ifndef GENERICLIST_CPP
 6    #define GENERICLIST_CPP
 7    #include <iostream>
 8    #include <cstdlib>
 9    #include "genericlist.h" //This is not needed when used as we are using this file,
10                           //but the #ifndef in genericlist.h makes it safe.
11    using namespace std;
12    
13    namespace listsavitch
14    {
15        //Uses cstdlib:
16        template<class ItemType>
17        GenericList<ItemType>::GenericList(int max) : maxLength(max),
18                                                     currentLength(0)
19        {
20            item = new ItemType[max];
21        }
22    
23        template<class ItemType>
24        GenericList<ItemType>::~GenericList( )
25        {
26            delete [] item;
27        }
28    
29        template<class ItemType>
30        int GenericList<ItemType>::length( ) const
31        {
32            return (currentLength);
33        }
34    
35        //Uses iostream and cstdlib:
36        template<class ItemType>
37        void GenericList<ItemType>::add(ItemType newItem)
38        {
39            if ( full( ) )
40            {
41                cout << "Error: adding to a full list.\n";
42                exit(1);
43            }
44            else
45            {
46                item[currentLength] = newItem;
47                currentLength = currentLength + 1;
48            }
49        }
50    
51        template<class ItemType>
52        bool GenericList<ItemType>::full( ) const
53        {
54            return (currentLength == maxLength);
55        }
56    
57        template<class ItemType>
58        void GenericList<ItemType>::erase( )
59        {
60            currentLength = 0;
61        }
62    }//listsavitch
63    #endif // GENERICLIST_CPP Notice that we have enclosed all the template
64           // definitions in #ifndef. . . #endif. 

Also note that that the implementation of the overloaded insertion operator << has been placed in the header file rather than the implementation file. This may seem unusual, but it is quite common when using friend functions or operators within a template. Although we are defining << like it is a member of GenericList, recall that friend functions really exist outside the class and are part of the namespace. The compiler will have an easy time finding the implementation of << this way when the class is included from other files.

If you want to separate the implementation of the overloaded friend insertion operator << from the header, then it requires a little bit of extra work. We must make a forward declaration of the << operator which in turn requires a forward declaration of the GenericList class. Display 17.7 illustrates the required changes to genericlist.h while Display 17.8 illustrates the changes to genericlist.cpp, which simply has the additional implementation.

Display 17.7 Interface for the Class Template GenericList Without Implementation

 1    //This version moves the implementation of the overloaded <<
 2    //to the .cpp file, but requires adding some forward declarations.
 3    #ifndef GENERICLIST_H
 4    #define GENERICLIST_H
 5    #include <iostream>
 6    using namespace std;
 7    
 8    namespace listsavitch
 9    {
10        template<class ItemType>
11        class GenericList;
12        //We need a forward declaration of the GenericList template
13        //class for the friend header declaration that comes right after it.
14    
15        template<class ItemType>
16        ostream& operator <<(ostream& outs, const GenericList<ItemType>& theList);
17        //Forward declaration of the friend << for the definition inside the
18        //GenericList class below. These must be defined here since << is not
19        //a member of the class.
20    
21        template<class ItemType>
22      class GenericList
23      {
24          The rest of this class is identical to Display 17.4 except the overloaded
25         operator below has no implementation code and an additional <>.
26
27              friend ostream& operator << <>(ostream& outs,
28                                        const GenericList<ItemType>& theList);
29              //Overloads the << operator so it can be used to output the
30              //contents of the list.
31              //Note the <> needed after the operator (or function) name!
32              //The implementation is in genericlist.cpp (Display 17.8).
33          };
34    }//listsavitch
35    #endif //GENERICLIST_H

Display 17.8 Implementation of GenericList with Overloaded Operator

 1    //This is the implementation file: genericlist.cpp
 2    //This is the implementation of the class template named GenericList.
 3    //The interface for the class template GenericList is in the
 4    //header file genericlist.h.
 5    #ifndef GENERICLIST_CPP
 6    #define GENERICLIST_CPP
 7    #include <iostream>
 8    #include <cstdlib>
 9    #include "genericlist.h" //Not needed when used as we are using this file,
10                             //but the #ifndef in genericlist.h makes it safe.
11    using namespace std;
12    
13    namespace listsavitch
14    {
15            The rest of this file is identical to  Display 17.6  except for the
16            Implementation of <<.
17        template<class ItemType>
18        ostream& operator <<(ostream& outs, const GenericList<ItemType>& theList)
19        {
20            for (int i = 0; i < theList.currentLength; i++)
21                  outs << theList.item[i] << endl;
22            return outs;
23        }
24    }//listsavitch
25    #endif // GENERICLIST_CPP Notice that we have enclosed all the template
26           // definitions in #ifndef . . . #endif.

Self-Test Exercises

  1. Give the definition for the member function getElement for the class template Pair discussed in the section “Syntax for Class Templates.”

  2. Give the definition for the constructor with zero arguments for the class template Pair discussed in the section “Syntax for Class Templates.”

  3. Give the definition of a template class called HeterogeneousPair that is like the class template Pair discussed in the section “Syntax for Class Templates,” except that with HeterogeneousPair the first and second positions may store values of different types. Use two type parameters T1 and T2; all items in the first position will be of type T1, and all items in the second position will be of type T2. The single mutator function setElement in the template class Pair should be replaced by two mutator functions called setFirst and setSecond in the template class HeterogeneousPair. Similarly, the single accessor function getElement in the template class Pair should be replaced by two accessor functions called getFirst and getSecond in the template class HeterogeneousPair.

  4. Is the following true or false?

    Friends are used exactly the same for template and nontemplate classes.