CPP09 W1 M1: Static Polymorphism: Templates
Learning Objectives
At the end of this Module, you will be able to:- Explain how to achieve static polymorphic behavior through the use of templates
- Explain how to write generic code using templates
- Explain how to declare and implement function templates
- Explain how to declare and implement class templates
- Explain how to declare and implement class member function templates
Static Polymorphism
Introduction
Static polymorphic behavior is realized at compile time. A class template acts as a generic class definition from which new class types can be defined by a simple change of parameter types. Whereas a normal class is used to create objects, a class template is used to define new class types, from which objects are then created. The compiler uses the types you supply, combined with the class template, to create a wholly new class type.Understanding how to declare, implement, and use class and function templates will pay big dividends in many ways. Primarily, it provides you with a mechanism to write generic code. Learning to write generic code can potentially save you a lot of work.
Definition of Template
A template defines a related set of classes or functions. The related set of classes or functions defined by a template share the same code structure and functionality. The class or function template can then be used to declare a new class or function type.Function Templates
A function template is a generic function declaration and definition from which different versions of the function can be created by the compiler based on the argument types used to call the function. If you think this sounds a lot like overloaded functions you are right. Function templates and overloaded functions are related as you will soon see.
Class Templates
A class template is a generic class declaration and definition from which different, but related, class types can be created by the compiler based on type parameters.
Structure Templates
A structure template is like a class template but using structures instead.
How Templates Work: An Analogy
When you declare and define a template you are creating a generic version of whatever piece of code you are writing, be it a function or a class. In the declaration and definition of the template you will use one or more identifiers as type placeholders. These type placeholders are similar in function to the placeholders in a form letter generated with a word processor.The below figure illustrates a simple mail merge operation. A master letter is created with placeholders for certain data elements. The structure of the data source is mapped to the master letter by the placeholder identifiers name and age. When the mail merge function is executed, the data source is merged with the master letter to yield the finished letters. The master letter is a generic document that can be reused to generate many specific letter instances. Class and function templates work in similar fashion. A generic function or class is declared and defined. Placeholders are inserted into the code to reserve spots for actual data types. When specific versions of a template function are required the type substitutions are made based on the types of the arguments used to call the function. In the case of template classes, a special syntax is used when a new template class is declared.
Declaring And Using Function Templates
Up until now, if you wanted to create different versions of the same function to operate on different data types you would overload the function. For instance, if you wanted to declare a function named Sum() that took two arguments, added them together, and returned the result you could create several versions of the function like so:int Sum(int val1, int val2); float Sum(float val1, float val2); char Sum(char val1, char val2);
#ifndef SUM_TEMPLATE_H
#define SUM_TEMPLATE_H
template<class T> T Sum(T val1, T val2){
return val1 + val2;
}
#endif
To use the Sum() function template The Sum() function is called the same way as normal functions are called. The example below shows a main() function using the Sum() function on different data types.
#include
#ifndef SUM_TEMPLATE_H
#define SUM_TEMPLATE_H
template<class T> T Sum(T val1, T val2){
return val1 + val2;
}
#endif
using namespace std;
int main(){
cout<<Sum(3, 25)<<endl;//Integer
cout<<Sum(3.456, 5.786)<<endl; //Float
cout<<Sum('a', 'b')<<endl; //Char
//cout<
return 0;
}
Using Multiple Placeholders
The Sum() function template declared and defined in the above example used one type placeholder named T to reserve type spots in the function. Because both of the Sum() function’s parameters are reserved with the same placeholder they must be of the same type when the function is called. To illustrate, let us see what happens when the Sum() function is called with an integer and a float argument as shown in the following line of code:cout<
Error : in function int main() line no 15: no matching function for call to `Sum(int, double)
When the Sum() function template is called with two different argument types an error results. This error was produced using the Dev C++ 4.9.9.2. One way to eliminate this error is to declare the Sum() function template to use two different placeholders.
#include
#ifndef SUM_TEMPLATE_H
#define SUM_TEMPLATE_H
template<class T, class U> T Sum(T val1, U val2){
return val1 + val2;
}
#endif
using namespace std;
int main(){
cout<<Sum(3, 25)<<endl;
cout<<Sum(3.456, 5.786)<<endl;
cout<<Sum('a', 'b')<<endl;
cout<<Sum(3, 3.5)<<endl;
return 0;
}
Refer to line 14 of the above example. Notice now that the Sum() function is called with the first argument an integer and the second argument a float. By using two placeholders in the function template the error produced by using two different argument types is eliminated. However, the result type of the Sum() function is dictated by the first argument type. Since the T placeholder is used to reserve the type spot for both the first parameter and the return type of the function, whatever type the first argument to the function happens to be will also be the return type of the function. In this example it is an integer. So, the result of calling the Sum() function with the arguments 3 and 3.5 is 6, not 6.5! Notice what happens when the order of the arguments are swapped.
To resolve the ambiguous return type issue simply declare yet another type place holder used specifically to dictate the function’s return type. The below example gives the revised Sum() function with the extra type placeholder V declared and used to reserve the return type.
#include
#ifndef SUM_TEMPLATE_H
#define SUM_TEMPLATE_H
template<class T, class U, class V> V Sum(T val1, U val2){
return val1 + val2;
}
#endif
using namespace std;
int main(){
cout<<Sum<int, int, int>(3, 25)<<endl;
cout<<Sum<double, double, double>(3.456, 5.786)<<endl;
cout<<Sum<char, char, char>('a', 'b')<<endl;
cout<<Sum<double, int, double>(3.5, 3)<<endl;
return 0;
}
Declaring And Using Class Templates
Class templates are used to declare and define a generic class structure. The compiler uses the class template and any types supplied via specialization to build a new class type. Let us begin the discussion of class templates with a simple Foo example. Example below shows the declaration and definition of a class template named Foo.#include
#ifndef FOOTEMPLATEDEF_H
#define FOOTEMPLATEDEF_H
template<class T> class Foo{
public:
Foo(T _val);
virtual ~Foo();
void setVal(T _val);
T getVal();
private:
T val;
};
template<class T> Foo<T>::Foo(T _val):val(_val){}
template<class T> Foo<T>::~Foo(){}
template<class T> void Foo<T>::setVal(T _val){
val = _val;
}
template<class T> T Foo<T>::getVal(){
return val;
}
#endif
using namespace std;
int main(){
Foo<int> f1(1);
Foo<char> f2('d');
cout<<f1.getVal()<<endl;
cout<<f2.getVal()<<endl;
return 0;
}
Referring to line 33 of above example, notice how the Foo class template is specialized to use an int type. The important point to note in this example is that Foo
A More Complex Class Template Example
The below Example gives the source code for a template of the DynamicArray class.#include
#ifndef _DYNAMIC_ARRAY_H
#define _DYNAMIC_ARRAY_H
template<class T> class DynamicArray{
public:
DynamicArray(int _size = 5);
virtual ~DynamicArray();
T& operator[](unsigned i);
int getSize();
private:
T* its_array;
int size;
};
template<class T> DynamicArray<T>::DynamicArray(int _size):size(_size){
its_array = new T[_size];
for(int i=0; i<size; i++)
its_array[i] = static_cast<T>(0);
}
template<class T> DynamicArray<T>::~DynamicArray(){
delete[] its_array;
}
template<class T> T& DynamicArray<T>::operator[](unsigned i){
if(i >= (size)){
int newsize = size+10;
T* temp = new T[size];
for(int j = 0; j<size; j++){
temp[j] = its_array[j];
}
delete[] its_array;
its_array = new T[newsize];
for(int j = 0; j<size; j++){
its_array[j] = temp[j];
}
for(int j=size; j<newsize; j++){
its_array[j] = static_cast<T>(0);
}
delete[] temp;
size = newsize;
return its_array[i];
}
else
return its_array[i];
}
template<class T> int DynamicArray<T>::getSize(){ return size;}
#endif
using namespace std;
int main(){
DynamicArray<char> d1;
DynamicArray<float> d2;
for(int i=0; i<6; i++){
d1[i] = 'a';
}
for(int i=0; i<6; i++){
d2[i] = (i + .5);
}
for(int i=0; i<d1.getSize(); i++){
cout<<d1[i]<<" "<<d2[i]<<endl;
}
return 0;
}
Resources
/ Fig.�11.2: tstack1.h // Stack class template. #ifndef TSTACK1_H #define TSTACK1_H template<> class Stack { public: Stack( int = 10 ); // default constructor (stack size 10) // destructor ~Stack() { delete [] stackPtr; } // end ~Stack destructor bool push( const T& ); // push an element onto the stack bool pop( T& ); // pop an element off the stack // determine whether Stack is empty bool isEmpty() const { return top == -1; } // end function isEmpty // determine whether Stack is full bool isFull() const { return top == size - 1; } // end function isFull private: int size; // # of elements in the stack int top; // location of the top element T *stackPtr; // pointer to the stack }; // end class Stack // constructor template<> Stack<>::Stack( int s ) { size = s > 0 ? s : 10; top = -1; // Stack initially empty stackPtr = new T[ size ]; // allocate memory for elements } // end Stack constructor // push element onto stack; // if successful, return true; otherwise, return false template<> bool Stack<>::push( const T &pushValue ) { if ( !isFull() ) { stackPtr[ ++top ] = pushValue; // place item on Stack return true; // push successful } // end if return false; // push unsuccessful } // end function push // pop element off stack; // if successful, return true; otherwise, return false template<> bool Stack<>::pop( T &popValue ) { if ( !isEmpty() ) { popValue = stackPtr[ top-- ]; // remove item from Stack return true; // pop successful } // end if return false; // pop unsuccessful } // end function pop // Fig.�11.3: fig11_03.cpp // Stack-class-template test program. #includeusing std::cout; using std::cin; using std::endl; #include "tstack1.h" // Stack class template definition int main() { Stack<> doubleStack( 5 ); double doubleValue = 1.1; cout << "Pushing elements onto doubleStack\n"; while ( doubleStack.push( doubleValue ) ) { cout << doubleValue << ' '; doubleValue += 1.1; } // end while cout << "\nStack is full. Cannot push " << doubleValue << "\n\nPopping elements from doubleStack\n"; while ( doubleStack.pop( doubleValue ) ) cout << doubleValue << ' '; cout << "\nStack is empty. Cannot pop\n"; Stack<> intStack; int intValue = 1; cout << "\nPushing elements onto intStack\n"; while ( intStack.push( intValue ) ) { cout << intValue << ' '; ++intValue; } // end while cout << "\nStack is full. Cannot push " << intValue << "\n\nPopping elements from intStack\n"; while ( intStack.pop( intValue ) ) cout << intValue << ' '; cout << "\nStack is empty. Cannot pop\n"; return 0; } // end main #endif 2.http://www.functionx.com/cpp/Lesson07.htm
Steps to attack the problem sets
- Step 1: Read about Static Polymorphism and try sample programs(DynamicArray) given in Static Polymorphism(You can copy paste the programs)
- Step 2: Go through the PPT on Templates and try stack program from the PPT.(you can copy paste the program)
- Step 3: Go through the additional resources given.
- Step 4: Attack the problem sets in the given order(Problem Set A and then Problem Set B ...).
- Step 5: If you are not clear with the problem sets then contact your respective mentor for clarification.
Problem Sets
Problem Set A
1. Create a function called swaps() that interchanges the values of the two arguments sent to it. (You will probably want to pass these arguments by reference.) Make the function into a template, so it can be used with all numerical data types (char, int, float, and so on). Write a main() program to exercise the function with several types.Name the program as: PA1_swaps.cpp
Problem Set B
2. Create a function called amax() that returns the value of the largest element in an array. The arguments to the function should be the address of the array and its size. Make this function into a template so it will work with an array of any numerical type. Write a main() program that applies this function to arrays of various types.Name the program as:PB1_amax.cpp
Problem Set C
3. A queue is a data-storage device. It’s like a stack, except that, instead of being last-in first-out, it’s first-in-first-out, like the line at a bank teller’s window. If you put in 1, 2, 3,you get back 1, 2, 3 in that order.A stack needs only one index to an array (i.e top of the array). A queue, on the other hand, must keep track of two indexes to an array: one to the tail, where new items are added, and one to the head, where old items are removed. The tail follows the head through the array as items are added and removed. If either the tail or the head reaches the end of the array, it is reset to the beginning.
Write a class template for a queue class. Assume that the programmer using the queue won’t make any mistakes, like exceeding the capacity of the queue or trying to remove an item when the queue is empty. Define several queues of different data types and insert and remove data from them.
Use the dynamicarray discussed previously for implementing the queue.
Write the declaration and defining of Queue template in separate header file and a Test program to test the queue in separate source file.
Comments
Post a Comment