Inheritance and containment are good, but they aren't always the answer to a desire to reuse code.
For example, we defined a function that swapped two int values. Something likes:
void swap(int &a, int &b);If we want to swap two double value instead of int, then, one approach is to duplicate the original code, but replace each int with double
Now, if we need to swap two chars, then, we have to duplicate again. At the end of the day, we have three set of similar code to maintain and change ...
C++'s function template capability automates the process, saving you time and providing greater reliability
// Format exampletemplate <class Any> // Set up template, and name it arbitray type Any // FYI - A lot of people like just a name Tvoid swap(Any &a, Any &b) // Swap logic { Any temp; temp = a; a = b; b = temp; };If we want a function to swap int, then the compiler will create a function following the template pattern, substituting int for Any. Similarly, if you need a function to swap doubles, the compiler will follow the template, substituting the double type for Any
Use templates if you need functions that apply the same algorithm to a variety of types
Many C++ compilers do not yet support templates at the time of this writing. Also, until the final version of the ANSI/ISO C++ standard comes out (when ?), template details are subject to change.
// Example:// funtemp.cpp -- using a function template #include <iostream.h> // function template prototype template <class Any> void swap(Any &a, Any &b); int main(void) { int i = 10, j = 20; cout << "i, j = " << i << ", " << j << ".\n"; cout << "Using compiler-generated int swapper:\n"; swap(i,j); // generates void swap(int &, int &) cout << "Now i, j = " << i << ", " << j << ".\n"; double x = 24.5, y = 81.7; cout << "x, y = " << x << ", " << y << ".\n"; cout << "Using compiler-generated double swapper:\n"; swap(x,y); // generates void swap(double &, double &) cout << "Now x, y = " << x << ", " << y << ".\n"; return 0; } // function prototype definition template <class Any> void swap(Any &a, Any &b) { Any temp; temp = a; a = b; b = temp; };
If you want to use the same kind of function to do the function call, instead of using a compiler generated template function. Then, a specification will be useful
// Old Form Example // Depends on what version of compiler you use, we might have old form // or new formtemplate <class Any> void swap(Any &a, Any &b); // template prototype void swap(int &n, int &m); // regular prototype - specification int main(void) { double u, v; ... swap(u, v); // use template int a, b; ... swap(a, b); // use void swap(int &, int &) }The original template facility called for the compiler to use the nontemplate version, treating it as a specialization of the template
// New Form Example // Some says old form is bad, because if you include some header files // from other people, you might not have full control over which function // will get call// We use the new form to do:template <class Any> void swap(Any &a, Any &b); // template prototype void swap<int>(int &n, int &m); // specialization prototype int main(void) { double u, v; ... swap(u, v); // use template int a, b; ... swap(a, b); // use void swap<int>(int &, int &) }Note that the <int> appears in the prototype, not in the function call. It also should appear in the function definition
The concept of function templates can be pushed one level higher to become class templates
Suppose we have an ArrayDB class that will store integers and have methods to deal with them ... now, it will be nice if we could use the same code to store double, float or char or string
C++'s class templates provide a good way to generate generic class declarations
Different level of compilers implement different level of tempalte support
Templates provide parameterized types, that is, the capability of passing a type name as an argument to an algorithm for building a class or a function. By feeding the type name int to a ArrayDB template, for example, you can get the compiler to construct a ArrayDB class for dealing with ints
The following example shows the combined class and member function templates
It is important to realize that these are not class and member function definitions, they are instructions to the C++ compiler about how to generate class and member functions
A particular actualization of a template, such as a stack class for handling String objects, is called an instantiation
If you separate the template member functions in a separate implementation file, most compilers will not compile
The simplest way to make this work is to place all the template information in a header file and to include the header file in the file that will use the templates
Example:
// stacktp.h #include "booly.h" template <class Type> class Stack { private: enum {MAX = 10}; // constant specific to class Type items[MAX]; // holds stack items int top; // index for top stack item public: Stack(); Bool isempty(); Bool isfull(); Bool push(const Type & item); // add item to stack Bool pop(Type & item); // pop top into item }; template <class Type> Stack<Type>::Stack() { top = 0; } template <class Type> Bool Stack<Type>::isempty() { return top == 0? True: False; } template <class Type> Bool Stack<Type>::isfull() { return top == MAX? True :False; } template <class Type> Bool Stack<Type>::push(const Type & item) { if (top < MAX) { items[top++] = item; return True; } else return False; } template <class Type> Bool Stack<Type>::pop(Type & item) { if (top > 0) { item = items[--top]; return True; } else return False; }
// stacktem.cpp -- test template stack class // compiler with strng2.cpp #include <iostream.h> #include <ctype.h> #include "stacktp.h" #include "strng2.h" int main(void) { Stack<String> st; // create an empty stack char c; String po; cout << "Please enter A to add a purchase order,\n" << "P to process a PO, or Q to quit.\n"; while (cin >> c && toupper(c) != 'Q') { while (cin.get() != '\n') continue; if (!isalpha(c)) { cout << '\a'; continue; } switch(c) { case 'A': case 'a': cout << "Enter a PO number to add: "; cin >> po; if (st.isfull()) cout << "stack already full\n"; else st.push(po); break; case 'P': case 'p': if (st.isempty()) cout << "stack already empty\n"; else { st.pop(po); cout << "PO #" << po << " popped\n"; break; } } cout << "Please enter A to add a purchase order,\n" << "P to process a PO, or Q to quit.\n"; } cout << "Bye\n"; return 0; }
Node.hpp (class definition and implementation)
LinkedList.hpp (template class definition)