13.3. Template Classes

template, data structure (definition), container, multiple inheritance
Time: 00:03:00 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides: PDF, PPTX
Review

Just as a template implementation only benefits select functions, a template implementation only benefits select classes. Data structures, called containers in C++ programs, are a prime example. "A data structure is a specialized format for organizing, processing, retrieving, and storing data. There are several basic and advanced types of data structures, all designed to arrange data to suit a specific purpose." Initially, C++ formed containers with multiple inheritance, inheriting "knowledge" of the container from one superclass and "knowledge" of the data from a second superclass. Today, it implements containers as classes with one or more template variables that specify the type of the contained data.

Some data structures or containers are simple enough for compilers to implement them directly in computer programming languages. For example, most languages support arrays, data files, structures, and classes as language primatives. Other data structures, such as stacks, lists, trees, and hash tables, are too complex to be language primitives. Nevertheless, these complex data structures are too useful to ignore, so programming languages typically provide them as classes organized into language libraries. Although the list of helpful data structures is long and varied, they all utilize the same basic template syntax.

Making And Using Template Classes

template, template keyword, class keyword, stack class, stack.h, template class, template variable, data structure, container, boilerplate code, throw

A stack is a classic data structure that is simple, yet complex enough to demonstrate template-based containers authentically. The text introduced stacks in detail in the arrays chapter and used them in subsequent examples, the most recent and relevant as a stack class demonstrating the this pointer. Basing a stack on templates is a simple upgrade that allows it to hold or contain any program-specified data type. The following figure illustrates the fundamental syntax and features of a template class.

While the current ANSI standard allows programmers to choose either "class" or "typename" to specify template variables, the former seems appropriate for creating template classes. Furthermore, as illustrated later in the chapter, C++ documentation typically uses the "class" keyword.
#include <iostream>
using namespace std;

template <class T>
class stack
{
    private:
        static const int SIZE = 100;

        T    st[SIZE];
        int  sp = 0;

    public:
        void push(T data);
        T    pop();
        int  size();
        T    peek();
};
template <class T>
void stack<T>::push(T data)
{
    if (sp < SIZE)
        st[sp++] = data;
    else
        throw "Stack Overflow";
}


template <class T>
T stack<T>::pop()
{
    if (sp > 0)
        return st[--sp];
    else
        throw "Stack Underflow";
}
template <class T>
int stack<T>::size()
{
    return sp;
}


template <class T>
T stack<T>::peek()
{
    return st[sp - 1];
}
stack.h: a template version of the stack class. Implementing a stack as a template class demonstrates that the container's operations are independent of the data type. The yellow-highlighted text is "boilerplate code" activating the template mechanism and introducing the template variable, T. The template variable represents the contained data type throughout the class and its member functions. Crucially, the compiler can't compile the functions before expanding (i.e., replacing) the template variable with the program-specified type, so programmers place the class specification and functions in a header file. The green-highlighted text becomes part of each function name, which, after expansion, becomes part of the class name, distinguishing between classes and functions processing different data types.

 

#include  <iostream>
#include  "stack.h"
using  namespace  std;

int main()
{
	stack<int>  s;

	s.push(10);
	s.push(20);
	s.push(30);

	cout << s.pop() << endl;
	cout << s.pop() << endl;
	cout << s.pop() << endl;

	return 0;
}
 
 
 
 
#include <iostream>
#include "Person.h"
#include "stack.h"
using  namespace  std;

int main()
{
	stack<Person>	p;
	Person		x("Alice");
	Person		y("Dilbert");
	Person		z("Wally");

	p.push(x);
	p.push(y);
	p.push(z);

	p.pop().display();
	p.pop().display();
	p.pop().display();

	return 0;
}
(a)(b)
Using a template class. When a C++ program defines a variable as a template-class type, it must provide a concrete data type as part of the definition. The type name replaces the template variable throughout the template class and member functions. The compiler compiles the code after it replaces the template variable with the concrete, program-supplied data type.
  1. Defining variable s, and instantiates a stack container storing integers.
  2. Defining variable p, and instantiates a stack container storing instances of the Person class. The variables x, y, and z are instances of the Person class, initialized with a general constructor.
Stacks s and p are instances of different classes: s is an instance of stack<int>, while p is an instance of <Person>.

Template Constant-Value Expressions

template, template keyword, class keyword, stack class, template constant, default, throw

Generalizing a stack by making it a template class greatly increases its utility. However, it still suffers from a significant weakness: specifying the stack size using the SIZE constant makes it inflexible. Specifically, it wastes space when only a small stack is needed and fails when a larger one is needed. Chapter 6 demonstrated that functions can specify default values for their arguments, and that programs accept or override those values. Templates provide programmers with the same flexibility when creating template classes. This approach requires modifying the template application code that specifies the container.

Template Constant Without A DefaultTemplate Constant With A Default
Template Code
template <class T, int SIZE>
Application Code
stack<int, 10> s;
Template Code
template <class T, int SIZE = 100>
Application Code
stack<int, 10> s;
stack<int> s;
(a)(b)
Creating and using template constants.
  1. A template class that requires programmers to specify a data type and a stack size.
  2. A template class with a default size. Programmers must specify a data type, but may override or accept the default size.

 

#include <iostream>
using namespace std;

template <class T, int SIZE = 100>
class stack
{
    private:
        T    st[SIZE];
        int  sp = 0;

    public:
        void push(T data);
        T    pop();
        int  size();
        T    peek();
};
template <class T, int SIZE>
void stack<T, SIZE>::push(T data)
{
    if (sp < SIZE)
        st[sp++] = data;
    else
        throw "Stack Overflow";
}

template <class T, int SIZE>
T stack<T, SIZE>::pop()
{
    if (sp > 0)
        return st[--sp];
    else
        throw "Stack Underflow";
}
template <class T, int SIZE>
int stack<T, SIZE>::size()
{
    return sp;
}

template <class T, int SIZE>
T stack<T, SIZE>::peek()
{
    return st[sp - 1];
}
Stack size specified by a template argument. In this version of the stack class, a template variable with a default value replaces the symbolic constant static const int SIZE = 100;. An application program using this version of the stack class can accept or override the default value, as illustrated in the next figure. While SIZE becomes part of the functions' template statements, the default value, = 100, only appears in the class specification.

 

#include  <iostream>
#include  "stack.h"
using  namespace  std;

int main()
{
	stack<int, 10>	s;
	stack<double>	d;

	s.push(10);
	s.push(20);
	d.push(2.7);
	d.push(3.1459);

	cout << s.pop() << endl;
	cout << s.pop() << endl;
	cout << d.pop() << endl;
	cout << d.pop() << endl;

	return 0;
}
Creating an instance of a class with constant value expressions. When application code uses a template class, it must specify replacements for every template variable that doesn't have a default. When a template variable has a default, the application can accept or override it.