It’s possible to understand the concept of a stack (a data structure into which we insert items only at the top and retrieve those items only from the top in last-in, first-out order) independent of the type of the items being placed in the stack. We discussed stacks in Section 6.11 where we presented the function-call stack. To instantiate a stack, a data type must be specified. This creates a nice opportunity for software reusability—as you already saw with the stack
container adapter in Section 15.7.1. Here, we define a stack generically then use type-specific versions of this generic stack class.
Class templates encourage software reusability by enabling a variety of type-specific class-template specializations to be instantiated from a single class template.
Class templates are called parameterized types, because they require one or more type parameters to specify how to customize a generic class template to form a class-template specialization. To produce many specializations you write only one class-template definition (as we’ll do shortly). When a particular specialization is needed, you use a concise, simple notation, and the compiler writes the specialization source code. One Stack
class template, for example, could thus become the basis for creating many Stack
class-template specializations (such as “Stack
of double
s,” “Stack
of int
s,” “Stack
of Employee
s,” “Stack
of Bill
s,” “Stack
of ActivationRecord
s,” etc.) used in a program.
To create a template specialization with a user-defined type, the user-defined type must meet the template’s requirements. For example, the template might compare objects of the user-defined type with <
to determine sorting order, or the template might call a specific member function on an object of the user-defined type. If the user-defined type does not overload the required operator or provide the required functions, compilation errors occur.
The Stack
class-template definition in Fig. 18.2 looks like a conventional class definition, with a few key differences. First, it’s preceded by line 7
template<typename T>
All class templates begin with keyword template
followed by a list of template parameters enclosed in angle brackets (< and >); each template parameter that represents a type must be preceded by either of the interchangeable keywords typename
or class
(though type-name
is preferred). The type parameter T
acts as a placeholder for the Stack
’s element type. The names of type parameters must be unique inside a template definition. You need not specifically use identifier T
—any valid identifier can be used. The element type is mentioned generically throughout the Stack
class-template definition as T
(lines 11, 16 and 36). The type parameter becomes associated with a specific type when you create an object using the class template—at that point, the compiler generates a copy of the class template in which all occurrences of the type parameter are replaced with the specified type. Another key difference is that we did not separate the class template’s interface from its implementation.
Templates are typically defined in headers, which are then #include
d in the appropriate client source-code files. For class templates, this means that the member functions are also defined in the header—typically inside the class definition’s body, as we do in Fig. 18.2.
Stack<T>
’s Data RepresentationSection 15.7.1 showed that the Standard Library’s stack
adapter class can use various containers to store its elements. Of course, a stack
requires insertions and deletions only at its top. So, for example, a vector
or a deque
could be used to store the stack
’s elements. A vector
supports fast insertions and deletions at its back. A deque
supports fast insertions and deletions at its front and its back. A deque
is the default representation for the Standard Library’s stack
adapter because a deque
grows more efficiently than a vector
. A vector
is maintained as a contiguous block of memory—when that block is full and a new element is added, the vector
allocates a larger contiguous block of memory and copies the old elements into that new block. A deque
, on the other hand, is typically implemented as a list of fixed-size, built-in arrays—new fixed-size built-in arrays are added as necessary and none of the existing elements are copied when new items are added to the front or back. For these reasons, we use a deque
(line 36) as the underlying container for our Stack
class.
Stack<T>
’s Member FunctionsThe member-function definitions of a class template are function templates, but are not preceded with the template
keyword and template parameters in angle brackets (<
and >
) when they’re defined within the class template’s body. As you can see, however, they do use the class template’s template parameter T
to represent the element type. Our Stack
class template does not define it’s own constructors—the default constructor provided by the compiler will invoke the deque
’s default constructor. We also provide the following member functions in Fig. 18.2:
top
(lines 11–13) returns a const
reference to the Stack
’s top element.
push
(lines 16–18) places a new element on the top of the Stack
.
pop
(lines 21–23) removes the Stack
’s top element.
isEmpty
(lines 26–28) returns a bool
value—true
if the Stack
is empty and false
otherwise.
size
(lines 31–33) returns the number if elements in the Stack
.
Each of these member functions calls the appropriate member function of class template deque
—this is known as delegating.
Though we did not do so in our Stack
class template, member-function definitions can appear outside a class template definition. If you do this, each must begin with the template
keyword followed by the same set of template parameters as the class template. In addition, the member functions must be qualified with the class name and scope resolution operator. For example, you can define the pop
function outside the class-template definition as follows:
template<typename T>
inline void Stack<T>::pop() {
stack.pop_front();
}
Stack<T>::
indicates that pop
is in the scope of class Stack<T>
. The Standard Library’s container classes tend to define all their member functions inside their class definitions.
Stack<T>
Now, let’s consider the driver (Fig. 18.3) that exercises the Stack
class template. The driver begins by instantiating object doubleStack
(line 8). This object is declared as a Stack<double>
(pronounced “Stack
of double
”). The compiler associates type double
with type parameter T
in the class template to produce the source code for a Stack
class with elements of type double
that actually stores its elements in a deque<double>
.
Lines 15–19 invoke push
(line 16) to place the double
values 1.1, 2.2, 3.3, 4.4 and 5.5 onto doubleStack
. Next, lines 24–27 invoke top
and pop
in a while
loop to remove the five values from the stack. Notice in the output of Fig. 18.3 that the values pop
off in last-in, first-out order. When doubleStack
is empty, the pop
loop terminates.
Line 31 instantiates int
stack intStack
with the declaration
Stack<int> intStack;
(pronounced “intStack
is a Stack
of int
”). Lines 38–41 repeatedly invoke push
(line 39) to place values onto intStack
, then lines 46–49 repeatedly invoke top
and pop
to remove values from intStack
until it’s empty. Once again, notice in the output that the values pop
off in last-in, first-out order.